Skip to main content

Troubleshooting

Most inputs diff quickly, but some are slow, and some edge cases produce results that need explanation. Each issue below lists the symptom, the cause, and a workaround.

Diff is too slow

Symptom: Diffing a large tree or monorepo takes much longer than expected. Cause: Container matching at the bottom of the tree is O(n²) in the worst case. A 250,000-node safety valve (maxNodes) caps the tree size, so very large inputs are truncated rather than diffed exhaustively. Workaround: Check the file sizes involved, and diff specific files instead of whole directories. On a large repository, diffing one file is much faster than diffing a directory or the whole working tree.
The 250k maxNodes limit is intentional, not a bug. It keeps diffs bounded on pathological inputs. Improvements to bottom-up container matching scaling are tracked in the roadmap.

Deep nesting causes a crash

Symptom: A stack overflow or crash when diffing heavily nested structures, such as generated JSON or deeply nested code. Cause: Recursion depth in the matcher. Content nested deeper than roughly 10,000 levels can exhaust the call stack. Workaround: A fix is in progress: the matcher will use iterative stacks instead of recursive tree walks. In the meantime, split the input into smaller pieces, or diff at a coarser level. For example, diff a subtree instead of the whole file.

YAML diffs are wrong

Symptom: YAML diffs lose nesting, report spurious changes, or show flat structures that should be nested. Cause: The YAML parser requires 2-space indentation. YAML formatted with 4-space indentation (or tabs) can lose its nesting structure during parsing. Workaround: Reformat YAML to 2-space indentation before diffing.
YAML with non-standard indentation (4 spaces, tabs) may parse with collapsed or shifted nesting, producing misleading diffs. Convert to 2-space indentation for correct results.

Renames show as delete + insert

Symptom: A renamed function or variable appears as a pair of Delete and Insert actions instead of a rename. Cause: Rename detection at the leaf level is a known limitation. Individual leaf nodes do not carry enough context for the matcher to pair them as renames. Workaround: This is largely cosmetic. The narration layer (@ossl-dev/differens-narrate) reads the surrounding context and phrases such changes as moves or renames in its prose output. That is the intended way to consume diffs, whether by humans or LLMs.

Markup parser misbehaves

Symptom: HTML/XML diffs are wrong, especially around attributes. Cause: The markup tokenizer (T3 tier) is regex-based, and it can misparse a > that appears inside an attribute value, for example <div title="a > b">. The tokenizer treats the > as closing the tag. Workaround: Rewrite the attribute value so it does not contain a raw > (for example, use &gt; or &#62;), or diff the file with a different tier.
Attribute values containing a bare > can confuse the markup tokenizer, producing truncated or malformed trees. Escape them as &gt; to be safe.

Build errors

Symptom: bun run build fails with missing module or resolve errors. Cause: Dependencies were never installed, or the installed Bun version is too old. Differens requires Bun >= 1.3. Workaround:
If you upgraded Bun, run bun install again to refresh the lockfile and native bindings.

TypeScript errors

Symptom: Editor or CI reports type errors across packages. Cause: Stale build artifacts, or a change that broke a shared type across the monorepo. Workaround: Run the typechecker across all packages to see the full picture:
If errors persist after a clean typecheck, the packages depend on each other through the workspace. Rebuild the dependents:

Still stuck?

Open a GitHub issue at ossl-dev/differens with the command you ran, the input shape (file, directory, or commit range), and the output you saw. Known limitations are tracked in ROADMAP.md at the repo root.