Sitelet https://fallow.tools/docs/cli/trace/
Skip to content
Fallow home
All docs pages

fallow trace

See what calls an exported symbol and what it calls before you change it, or find how one module comes to import another. Fallow trace is a best-effort command, separate from the ranked review brief.

Before you change an exported symbol, use fallow trace to see how it connects to the rest of the codebase. The command walks the call chain of one symbol in two directions, up to --depth:

  • Callers, up: the modules that import the symbol.
  • Callees, down: the import-symbol edges, plus the call sites in the same module.
fallow trace src/api.ts:fetchUser --format json --quiet
fallow trace src/api.ts:fetchUser --callers --depth 2
fallow trace --path src/app.ts src/db.ts

--path answers a different question: which chain of imports connects one module to another. See Import path between two modules.

fallow trace is a standalone, best-effort command. Its results are not part of the ranked review brief, and they never change the focus map or its ranking. Use trace to see how a symbol connects. For a prioritized review, use the review brief.

Target

fallow trace takes one positional argument in FILE:SYMBOL form:

fallow trace src/api.ts:fetchUser

FILE is a project-relative path, and SYMBOL is the exported symbol name. When you pass --path, omit this argument. --path takes two file paths.

When a file exists at the exact path from the project root, fallow takes that file. src/api.ts then never selects packages/x/src/api.ts. When no file has the exact path, fallow accepts a path suffix. A short path that matches more than one file takes the first match, so use the full project-relative path in a monorepo.

Options

FlagDescription
--callersWalk only the callers direction.
--calleesWalk only the callees direction.
--depth <N>Maximum walk depth in each requested direction (default: 2).
--path <FROM> <TO>Find the shortest import path between two modules. You cannot use it with the FILE:SYMBOL target, --callers, --callees, or --depth.
--eager-onlyWith --path, follow only static imports that carry a runtime value. The route shows why TO loads before FROM runs.

Without --callers or --callees, fallow walks both directions. trace also accepts the project, output, and performance global flags: --root, --config, --format with human or json, --quiet, --no-cache, --threads, and --changed-since.

How it works

The walk is best-effort and syntactic. Fallow reports resolved and unresolved callees. An unresolved callee, for example a dynamic call that fallow cannot resolve statically, stays in the output as unresolved. The chain thus shows what fallow could follow and what it could not.

When code references the same export in type space and in value space, the JSON output adds direct_references_by_namespace. This field has separate evidence for each namespace. For compatibility, namespace and direct_references keep their earlier meaning: they describe only the winning namespace. When only one namespace has references, fallow omits direct_references_by_namespace. Read a missing field as the usual case of one namespace.

Ambiguous star exports

When two export * sources both give the requested name, the barrel exports nothing under that name. The JSON output reports symbol_found: false and adds a star_export_ambiguity object:

{
  "symbol_found": false,
  "star_export_ambiguity": {
    "sources": ["src/models/admin.ts", "src/models/customer.ts"],
    "namespaces": ["type"]
  }
}

sources lists the files that declare the name, sorted, as project-relative paths. namespaces tells you if the collision is in type space, value space, or both, with type before value. With this object, you can tell a barrel collision from an unknown or misspelled symbol. To fix the collision, keep one origin and rename the others, or replace the star exports with explicit re-exports.

You can also get the same best-effort call-chain data as opt-in evidence from fallow inspect --symbol-chain and from the symbol_chain option of the inspect_target MCP tool.

Import path between two modules

Use --path to find out why a module depends on another module. fallow trace --path <FROM> <TO> reports the shortest chain of imports from FROM to TO. Both are project-relative file paths.

An exact project-relative or absolute path wins over a suffix match. Fallow accepts an abbreviation only when it identifies one module. A missing or ambiguous endpoint exits with code 2. To remove the ambiguity, use the full project-relative path.

Shortest import path (syntactic; OFF the ranked path)

  from: pages/report.tsx
  to:   components/Box.tsx
  hops: 3

  [1] pages/report.tsx:20 -> lib/related.transform.ts
  [2] lib/related.transform.ts:1 -> components/RelatedContent.tsx
  [3] components/RelatedContent.tsx:3 -> components/Box.tsx

The JSON output has the same walk under kind: "trace", with its own schema_version:

{
  "kind": "trace",
  "schema_version": "1",
  "from": "pages/report.tsx",
  "to": "components/Box.tsx",
  "reachable": true,
  "hops": 3,
  "path": [
    {
      "from": "pages/report.tsx",
      "to": "lib/related.transform.ts",
      "type_only": false,
      "dynamic": false,
      "import_line": 20
    },
    {
      "from": "lib/related.transform.ts",
      "to": "components/RelatedContent.tsx",
      "type_only": false,
      "dynamic": false,
      "import_line": 1
    },
    {
      "from": "components/RelatedContent.tsx",
      "to": "components/Box.tsx",
      "type_only": false,
      "dynamic": false,
      "import_line": 3
    }
  ],
  "reason": "pages/report.tsx reaches components/Box.tsx in 3 hops"
}

You can rely on these behaviors:

  • Fallow reports a type-only hop with type_only: true and does not skip it. A path that exists only in type space is thus visible.
  • Fallow reports a lazy hop with dynamic: true. The target of such a hop loads only on demand (import(), a lazy glob) or on another thread (a worker, a fork). The human output tags the hop [dynamic].
  • With --eager-only, the walk follows only static imports that carry a runtime value. import(), lazy globs, worker loads, and import type do not qualify. The route explains why a module is in the eager group of fallow list --entry-weight. When no such route exists, reachable is false.
  • An unreachable pair is a result, and not an error. reachable is false, path is empty, hops is 0, and reason says that fallow found no import path. The command still exits 0.
  • A trace from a module to itself returns reachable: true with hops: 0. The hop count is 0 in both cases, so branch on reachable.

See also