Edit tool silently normalizes `\uXXXX` escape sequences in old_string, breaking exact matches

Resolved 💬 7 comments Opened Apr 24, 2026 by wodin Closed May 31, 2026

Summary

The Edit tool appears to transparently convert between \uXXXX escape sequences and their literal Unicode codepoints when matching old_string against file contents. When the target file legitimately contains the six ASCII characters \, u, 0, 0, A, 0 (e.g., inside a JS/TS regex), no form of old_string matches — and there's no way to opt out.

Steps to reproduce

  1. Create a file repro.ts containing:

``ts
const key = s.replace(/\u00A0/g, ' ');
`
Verify the file literally contains backslash-u-0-0-A-0 via
od -c repro.ts` — six ASCII chars, not an NBSP codepoint.

  1. Invoke Edit with old_string equal to that same line (same six ASCII chars).
  1. Error returned:

``
String to replace not found in file.
(note: Edit also tried swapping \uXXXX escapes and their characters;
neither form matched, so the mismatch is likely elsewhere in old_string.)
``

The error message confirms the harness tried both the escape form and the literal-NBSP form — neither matched, because the input was presumably rewritten to the NBSP form before comparison while the file contains the ASCII-escape form.

Expected

old_string is matched as a raw byte sequence. If the caller passes six ASCII chars, the tool looks for six ASCII chars.

Actual

Something in the input-processing path rewrites \u00A0 (and presumably any \uXXXX) to the actual codepoint before comparison, making it impossible to match source code that contains the escape sequence as literal text. Workaround required shelling out to python3 / sed, or git checkout <path> when the edit was effectively a revert of uncommitted changes.

Impact

Common in real codebases — any regex over NBSP / ZWSP / control chars, unicode-escape tests, tokenizer fixtures, JSON-encoded strings in source. Rare enough to not bite often, painful and opaque when it does.

Suggested fix

Treat old_string as an opaque byte sequence. If "helpful" normalization is desired as a fallback, only apply it when the literal match fails, and surface _which_ form matched so the caller understands what happened.

Environment

  • Claude Code CLI - 2.1.119 (Claude Code)
  • Platform: Linux
  • Model: Opus 4.7

Related

  • #50483 — Read tool rendering \uXXXX inconsistently on Windows. Adjacent symptom (Edit failures after Read) but different root cause: in this report, the file bytes were verified via od -c before the Edit call, so Read was not misleading — the normalization is happening on Edit's old_string input itself.

View original on GitHub ↗

This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗