CORE JSC

International Technology Partnership

Roo Tools

Fixing Roo Code apply_diff Failures ("SEARCH block not found") and How to Prevent Them

The file on disk looks exactly like what the agent is trying to match, and apply_diff still rejects the edit with "SEARCH block not found." The content isn't actually identical — a stale read, an auto-formatter, or a line-ending mismatch has quietly drifted it out of sync since the file was last seen.

Core JSC Team·August 4, 2026
Roo Codeapply_diffTroubleshootingDeveloper ToolsDiff Matching

The Problem

Roo Code's apply_diff tool takes a SEARCH block meant to match existing file content exactly, plus a REPLACE block to swap it with. Most of the time it works cleanly — until it doesn't, failing with an error like "SEARCH block not found" or "content does not match," even though the file open in the editor looks, character for character, like what the SEARCH block contains. The agent typically responds by retrying the same diff, which fails again for the same reason, or falls back to rewriting the entire file, discarding the precision a targeted diff was meant to provide.

Why It Happens

The file changed on disk after the agent last read it

If a build tool, linter auto-fix, a human edit, or an earlier tool call in the same session modified the file after the agent's mental model of its contents was formed, the SEARCH block is matching against a version of the file that no longer exists. This is the single most common cause, and it's invisible from the agent's side unless it explicitly re-reads before diffing.

Editor-side auto-formatting runs between generations of the SEARCH block and the actual apply

A "format on save" setting, or a formatter running as part of a file-watcher pipeline, can rewrite whitespace, quote style, or line breaks moments after a file is written — meaning a diff generated against pre-format content will no longer match the post-format file by the time apply_diff actually runs against it, even though no human touched anything in between.

Line-ending mismatches (CRLF vs. LF) invalidate an otherwise-correct match

A SEARCH block built from content that used Unix line endings (\n) will not exactly match the same text if the file on disk uses Windows line endings (\r\n), or vice versa — a difference that's invisible when a diff is visually inspected but fails byte-for-byte comparison, especially on projects with mixed .gitattributes settings or a Windows checkout of a Unix-authored repo.

Overly large SEARCH blocks are fragile by construction

A SEARCH block spanning dozens of lines has to match every one of those lines exactly — including any whitespace anywhere inside the block — which means the probability of an unrelated, incidental drift somewhere in that range breaking the whole match goes up sharply with block size, even when the specific lines actually being changed are untouched.

The Fix

1. Re-read the file immediately before generating a diff, every time state might have changed

Never generate a SEARCH block from a read that happened several tool calls ago in the same turn sequence, especially after any build, lint, or format step ran. A fresh read costs one extra tool call and eliminates the most common failure mode outright.

2. Keep SEARCH blocks as small and unique as the edit allows

Match only the minimal, uniquely-identifying span of lines actually being changed, rather than including large surrounding context "to be safe." A smaller block has fewer opportunities for incidental drift to break the match, and a failed small diff is far cheaper to diagnose and retry than a failed large one.

3. When a diff fails, re-read and regenerate — never retry the identical diff

Retrying the exact same SEARCH block after a failure almost never succeeds, since nothing about the mismatch has changed between attempts. The correct recovery is to re-read the current file content, confirm what's actually different from what was assumed, and build a new diff against the real, current state.

4. Normalize line endings at the project level rather than working around them per-edit

# .gitattributes
* text=auto eol=lf

Enforcing consistent line endings across the repository removes an entire class of invisible mismatch, rather than requiring every diffing tool call to somehow account for which line-ending convention a given file happens to use.

Why This Works

Every one of these fixes addresses the same underlying requirement: apply_diff needs the SEARCH block to be byte-identical to what's actually on disk at the moment the diff is applied, not what was true when the agent formed its mental model of the file. A fresh read closes the staleness gap directly; small, unique blocks reduce the surface area where incidental drift can cause a mismatch; treating a failure as a signal to re-read (not retry) respects that the failure itself is evidence the assumption was wrong; and consistent line endings remove a source of invisible mismatch that has nothing to do with the actual content being changed.

Conclusion

An apply_diff failure with "SEARCH block not found" almost never means the tool is broken — it means the SEARCH block doesn't actually match the file's real, current content, usually because of a stale read, an auto-formatter, or a line-ending mismatch. Re-read before diffing, keep SEARCH blocks minimal and unique, treat a failure as a cue to re-read rather than retry, and normalize line endings at the repository level to eliminate the most invisible cause.