
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fallow.tools/llms.txt
> Use this file to discover all available pages before exploring further.

# Debugging & troubleshooting

> Trace export usage, diagnose false positives, and debug fallow's analysis pipeline with built-in tracing and performance profiling tools.

When fallow reports something unexpected, built-in tracing and performance tools help you understand *why*.

## Tracing export usage

Use `--trace FILE:EXPORT` to see the full usage chain for a specific export.

```bash theme={null}
fallow dead-code --trace src/utils/format.ts:formatCurrency
```

```bash title="$ fallow dead-code --trace src/utils/format.ts:formatCurrency" theme={null}
  UNUSED formatCurrency in src/utils/format.ts

  File: reachable
  Reason: No references found, export is unused
```

Prints every file that imports `formatCurrency`, including indirect usage through barrel re-exports. Use this when:

* An export you expected to be used is reported as unused.
* You want to understand which consumers depend on a symbol before removing it.
* A re-export chain is not being resolved as expected.

## Tracing file edges

Use `--trace-file PATH` to see all incoming and outgoing edges for a file in the module graph.

```bash theme={null}
fallow dead-code --trace-file src/components/Button.tsx
```

Shows every import the file makes and every file that imports from it. Use this when:

* A file is reported as unused but you believe something should reference it.
* You want to verify that a file is reachable from entry points.
* You're investigating why a file's exports are all marked unused (maybe the file itself is unreachable).

## Tracing dependency usage

Use `--trace-dependency PACKAGE` to find everywhere a package is used: imports, script binaries, and plugin detection.

```bash theme={null}
fallow dead-code --trace-dependency lodash
```

```bash title="$ fallow dead-code --trace-dependency moment" theme={null}
  UNUSED moment (0 import(s))
```

Reports all import sites, `package.json` script binary usage, and plugin-based detection for the given package. Use this when:

* A dependency is reported as unused but you believe it's used in scripts or config files.
* You want to audit which parts of your codebase depend on a specific package.
* You're evaluating whether a dependency can be removed.

## Tracing duplication clones

Use `dupes --trace FILE:LINE` to see all clone instances at a specific source location.

```bash theme={null}
fallow dupes --trace src/utils/validate.ts:42
```

Shows every other location that shares the same clone group as the code at the given line. Use this when:

* You want to find all copies of a duplicated block before refactoring.
* You're deciding whether to extract a shared function or module.
* A duplication finding seems incorrect and you want to inspect the matched instances.

## Performance profiling

Use `--performance` to get a timing breakdown of each pipeline stage.

```bash theme={null}
fallow dead-code --performance
fallow dupes --performance
```

```bash title="$ fallow dead-code --performance" theme={null}
┌─ Pipeline Performance ─────────────────────────────
│  discover files:       13.2ms  (520 files)
│  workspaces:            2.1ms  (0 workspaces)
│  plugin detection:     21.4ms  (+10.5ms plugin globs under entry points)
│  script analysis:       3.0ms
│  parse/extract:        17.6ms  (520 modules, 520 cached, 0 parsed)
│  cache update:          2.0ms
│  entry points:         12.3ms  (92 entries)
│    root package         1.5ms
│    workspaces           0.0ms
│    plugin globs        10.5ms
│      compile            9.9ms
│      match              0.2ms
│      (other)            0.4ms
│    infrastructure       0.2ms
│    dynamic globs        0.0ms
│    dedup                0.0ms
│    (other)              0.0ms
│  resolve imports:       0.0ms
│  build graph:           5.1ms
│  analyze:              19.3ms
│  (other):               0.0ms
│  ────────────────────────────────────────────────
│  TOTAL:                61.4ms
└───────────────────────────────────────────────────
```

The indented rows under `entry points` partition that stage rather than add to it, so they are not part of the `TOTAL` sum. They appear only when the stage cost at least 5ms, which keeps a fast project's box short. Use them when entry-point discovery is the expensive stage and you want to know whether the cost is plugin glob compilation, workspace traversal, or something else.

The `(other)` row is the time inside `TOTAL` not attributed to a named stage (report assembly and inter-stage glue), so the sequential stages plus `(other)` sum to `TOTAL`. In combined mode (`fallow` with no subcommand) the `duplication` stage runs concurrently with the rest of the pipeline and is marked `(concurrent)`; it is shown for reference but is not part of the `TOTAL` sum, so it can legitimately exceed `TOTAL` when duplication detection runs longer than the dead-code pass.

Use this when:

* Analysis is slower than expected and you want to find the bottleneck.
* You want to verify the cache is working (high hit rate means incremental runs are fast).
* You're comparing performance across different configuration options.

### Parallel parse timing

`parse/extract` runs across all cores. When the parse work was substantial and genuinely parallel, the line gains a `(parallel: ~Nms CPU)` suffix reporting the summed parse CPU across workers:

```bash title="$ fallow dead-code --performance (cold cache)" theme={null}
│  parse/extract:       382.4ms  (21033 modules, 0 cached, 21033 parsed)  (parallel: ~594ms CPU)
```

Here the stage finished in 382ms of wall-clock but spent \~594ms of CPU across cores. When that figure is much larger than the wall-clock the stage is CPU-bound and benefits from more cores; when it is close to the wall-clock the stage is I/O-bound and extra threads will not help. The suffix is omitted on warm or trivial runs where there is little real parse work to report.

In combined mode (`fallow` with no subcommand), the health breakdown reuses the discovered and parsed files from the dead-code pass, so its `discover files` and `parse/extract` rows read `(measured above)` and point you at the Pipeline Performance box where that cost is attributed.

In `--format json`, the timings carry `parse_cpu_ms` (summed parse CPU) alongside the wall-clock fields, and the health timings carry `shared_parse`. These are observational and vary run to run, so do not gate CI on them.

## Cache behavior

Fallow caches parsed AST data using bincode serialization with xxh3 hashing. On later runs, unchanged files load from cache instead of being re-parsed.

```bash theme={null}
# Run with caching (default)
fallow dead-code

# Skip the cache entirely
fallow dead-code --no-cache
```

Use `--no-cache` when:

* You suspect stale cache entries are causing incorrect results.
* You've changed fallow versions and want a clean analysis.
* You're benchmarking full parse performance.

The `--performance` flag shows cache statistics, including how many files were cache hits vs. misses. A high hit rate on incremental runs is expected. Only modified files need re-parsing.

### A refused cache says so

A run that had a cache but could not reuse it prints the reason under the stage that paid for it, so a refusal never reads like a first run:

```bash title="$ fallow dead-code --performance" theme={null}
│  parse/extract:        16.2ms  (335 modules, 0 cached, 335 parsed)  (parallel: ~98ms CPU)
│  parse cache not reused: cache format version changed
```

The module-graph cache reports the same way on its own line, `graph cache not reused`, under `build graph`. Neither line appears when the cache was reused, and neither appears under `--no-cache`, where you asked for the cold path. [`fallow doctor`](/cli/doctor) reports the same two states before you run an analysis.

## Common false positives

### Dynamic imports with runtime values

Fallow resolves template literals and `import.meta.glob`, but fully dynamic imports like `import(variable)` can't be resolved statically.

**Fix**: Add the target directory to `entry` in your config:

```jsonc theme={null}
{
  "entry": ["src/plugins/*.ts"]
}
```

### Convention-based exports

Some frameworks consume exports by naming convention rather than explicit imports. For example, Next.js `generateStaticParams` or Remix `loader` functions.

**Fix**: Fallow's built-in plugins already handle most frameworks. If you're using a niche framework, create a [custom plugin](/frameworks/custom-plugins) or use `ignoreExports`:

```jsonc theme={null}
{
  "ignoreExports": [
    { "file": "src/routes/**/*.ts", "exports": ["loader", "action"] }
  ]
}
```

### Dependency injection frameworks

DI containers (NestJS, Angular, InversifyJS) resolve dependencies at runtime via decorators and metadata. Fallow skips decorated class members by default, but injected services may look unused if they're only referenced via DI tokens.

**Fix**: Add the DI-registered files as entry points, or suppress specific findings:

```typescript theme={null}
// fallow-ignore-next-line unused-export
export class UserService { /* ... */ }
```

If your codebase uses utility decorators that DO NOT imply reflective use (Playwright `@step`, internal `@measure`, `@log`, etc.), opt them out of the skip via `ignoreDecorators` so methods carrying ONLY those names are checked for usage normally:

```jsonc theme={null}
// .fallowrc.json
{ "ignoreDecorators": ["@step"] }
```

A method carrying any decorator NOT in the list stays skipped, so `@step + @Inject` keeps the conservative DI-friendly behavior.

### Peer dependencies and optional dependencies

Packages listed in `peerDependencies` or `optionalDependencies` are not analyzed by default since they may be provided by the consuming project.

**Fix**: If fallow incorrectly flags these, add them to `ignoreDependencies`:

```jsonc theme={null}
{
  "ignoreDependencies": ["react", "react-dom"]
}
```

### Config files not recognized as entry points

If a config file (e.g., `tailwind.config.ts`) is reported as unused, its framework plugin may not be active.

**Fix**: Run `fallow list` to check which plugins are active. If the relevant plugin isn't detected, add the config file to `entry`:

```jsonc theme={null}
{
  "entry": ["tailwind.config.ts"]
}
```

## Missing findings

### Files in a hidden directory are missing

If a file under a dot-prefixed directory never appears in any finding, discovery
did not traverse it. Hidden directories are skipped apart from a few conventional
names and the ones an active framework plugin owns. Check the run's diagnostics:

```bash theme={null}
fallow dead-code --format json --quiet | jq '.workspace_diagnostics[] | select(.kind == "skipped-source-dotdir")'
```

**Fix**: No config field adds a directory to traversal. Analyze it on its own
with `fallow dead-code --root .claude`, or add it to `ignorePatterns` if it is
tool state you never want analyzed. See
[Known limitations](/analysis/limitations) for the full behavior.

## See also

<CardGroup cols={3}>
  <Card title="Dead Code Analysis" icon="skull-crossbones" href="/analysis/dead-code">
    Overview of how fallow detects unused code.
  </Card>

  <Card title="CLI: dead-code" icon="terminal" href="/cli/dead-code">
    Full reference for the `fallow dead-code` command and all its flags.
  </Card>

  <Card title="Inline Suppression" icon="comment-slash" href="/configuration/suppression">
    Suppress individual findings with inline comments.
  </Card>
</CardGroup>
