VSCode extension: diff preview fails (String not found in file) for multi-line edits on CRLF files

Status Open
Reported on v2.1.234
Maintainer reply None cached
Activity 0 comments · opened Aug 20, 2026

Environment

  • OS: Windows 11 Enterprise
  • Claude Code VSCode extension: v2.1.236 (also reproduced on locally-cached 2.1.234 / 2.1.235)
  • Repo line endings: CRLF (typical default on this Windows-based .NET project)

Summary

When the Edit tool's old_string spans two or more lines, and the target file uses CRLF line endings, the VSCode extension's diff-preview generation fails with:

Error processing client request: Error: String not found in file. Failed to apply edit.

The diff editor tab never renders. The user only sees a bare "Claude is requesting permission to use Edit" notification with no visual before/after diff. If the user clicks Allow anyway, the edit is applied correctly to disk (the underlying write path is unaffected) — so this is purely a review-UX bug, but it defeats the purpose of the diff-confirmation step, since there is nothing to visually review before approving.

Root cause (best guess)

The diff-preview code appears to do a literal substring search for old_string (whose line separator in the JSON payload is \n) against the file's actual on-disk bytes. When old_string spans multiple lines and the file uses \r\n, the search for "line1\nline2" fails against actual content "line1\r\nline2". Single-line old_string values never hit this because they contain no embedded newline to mismatch.

Reproduction matrix (tested across 9 edits on 6 different files / 3 file extensions: .cs, .json, .md)

| Line ending | old_string lines | Result | Log signature |
|---|---|---|---|
| CRLF | 1 (single-line) | ✅ diff renders | diff result undefined ... {"preview":false,"preserveFocus":true} |
| CRLF | 2–3 (multi-line) | ❌ diff fails, every time (6/6) | Error processing client request: Error: String not found in file. Failed to apply edit. |
| LF | 2–3 (multi-line) | ✅ diff renders (tested twice, add + revert) | diff result undefined ... (identical clean signature) |

The LF-file control test (.config/dotnet-tools.json, one of the few LF files in this repo) is the key data point: the same multi-line old_string that fails on CRLF files succeeds cleanly on an LF file, isolating CRLF line endings (not file extension, not whether the file was already open in an editor tab — both were also ruled out) as the necessary co-factor alongside multi-line old_string.

Steps to reproduce

  1. In a repo where a tracked file has CRLF line endings, ask Claude to edit that file such that the resulting old_string spans 2+ lines (e.g., insert a line before/after an existing multi-line anchor, or replace a multi-line block).
  2. Observe: no diff editor tab opens; only a plain "Claude is requesting permission to use Edit" notification appears.
  3. Check the extension's output log (Claude VSCode.log): Error processing client request: Error: String not found in file. Failed to apply edit. appears immediately after the open_diff request.
  4. Repeat with a single-line old_string on the same file → diff renders correctly.
  5. Repeat with a multi-line old_string on an LF-encoded file → diff renders correctly.

Impact

Any Windows/CRLF repository (very common) loses the visual diff-review step for the large fraction of edits that naturally span multiple lines (adding a line with context, replacing a block, etc.), silently degrading to an unreviewable allow/deny prompt. The edit itself still applies correctly once approved, so no data is lost — but users cannot verify what they're approving.

Secondary, less-confirmed observation

In one LF+multi-line success case, the diff request opened with "preserveFocus":true and the user did not notice the diff tab appear for ~49 seconds (vs ~10 seconds on a repeat of the same edit), despite the log showing an identical successful open. This may be an unrelated background-tab-focus issue where the diff tab doesn't come to the foreground — flagging it separately since it wasn't reliably reproduced (only observed once) and may be a distinct bug from the CRLF issue above.

---

This issue was discovered, reproduced, and written up by Claude Code (in a Claude Code session with the reporting user), based on live log analysis of the VSCode extension's own output during the investigation.

View original on GitHub ↗