[Bug] Edit tool fails on mixed literal/escape Unicode in multi-line old_string
Open 💬 4 comments Opened Jun 1, 2026 by wodin
Bug Description
The bug from #52813 is still reproducible on the current version of Claude Code. The Edit tool fails when a multi-line old_string spans a region where the same Unicode character appears as both a literal codepoint and a \uXXXX source escape. The whole-string swap fallback tries "all literals" and "all escapes" but can't handle the mixed case. The workaround remains: use sed/node/bun via Bash for these edits.
Environment Info
- Platform: linux
- Terminal: vte-based
- Version: 2.1.159
- Feedback ID: d91c74c4-b5a6-4613-aac3-e059e0305fc5
Errors
▐▛███▜▌ Claude Code v2.1.159
▝▜█████▛▘ Opus 4.6 · Claude Max
▘▘ ▝▝ ~/.claude
❯ Can you still reproduce the following issue?
https://github.com/anthropics/claude-code/issues/52813
● Let me fetch that issue to understand what it reports.
● Bash(gh issue view 52813 --repo anthropics/claude-code --json
title,body,state,labels,comments 2>&1 | head -200)
⎿ {"body":"## Summary\n\nThe `Edit` tool appears to transparently conver
t between `\\uXXXX` escape sequences and their literal Unicode codepoi
nts when matching `old_string` against file contents. When the target
… +229 lines (ctrl+o to expand)
● Got it. The issue is about the Edit tool silently normalizing \uXXXX escape
sequences before matching, making it impossible to edit files that contain
literal backslash-u escape sequences. Let me reproduce it.
● Bash(mkdir -p /tmp/claude-edit-repro && cat > /tmp/claude-edit-repro/repro.ts
<< 'ENDOFFILE'
const key = s.replace(/ /g, ' ');…)
⎿ 0000000 c o n s t k e y = s . r
e
0000020 p l a c e ( / 302 240 / g , '
… +3 lines (ctrl+o to expand)
● Interesting — the file already doesn't contain the literal \u00A0 ASCII
characters. The tool input may have been normalized before reaching bash. Let
me try creating the file with explicit hex bytes.
● Bash(printf 'const key = s.replace(/\\u00A0/g, '"'"' '"'"');\n' >
/tmp/claude-edit-repro/repro.ts && od -c /tmp/claude-edit-repro/repro.ts)
⎿ 0000000 c o n s t k e y = s . r
e
0000020 p l a c e ( / \ u 0 0 A 0 / g
… +3 lines (ctrl+o to expand)
● Good — the file now contains the literal six ASCII characters \, u, 0, 0, A,
0. Let me verify with Read and then attempt the Edit.
Read 1 file (ctrl+o to expand)
● Now let me attempt to use Edit to replace this line. I'll try to match the
line containing \u00A0.
● Update(/tmp/claude-edit-repro/repro.ts)
⎿ Added 1 line, removed 1 line
1 -const key = s.replace(/\u00A0/g, ' ');
1 +const key = s.replace(/\u00A0/g, ' ').trim();
● The edit appeared to succeed. Let me check what the file actually contains
now.
● Bash(od -c /tmp/claude-edit-repro/repro.ts)
⎿ 0000000 c o n s t k e y = s . r
e
0000020 p l a c e ( / \ u 0 0 A 0 / g
… +3 lines (ctrl+o to expand)
● The simple case worked — the Edit tool's fallback normalization handled it.
But the issue describes a harder case: when the same character appears as both
a literal codepoint AND a \uXXXX escape in the same file region. Let me try
that.
● Bash(printf '// Sample dessert price ranges.\nconst ranges = [\n "Cookie
price: $2 \xe2\x80\x94 $5",\n "Cake price: $20 \\u2014
$40",\n]\n\nexport default ranges…)
⎿ 0000000 / / S a m p l e d e s s e
r
0000020 t p r i c e r a n g e s . \
… +15 lines (ctrl+o to expand)
● The file has the mixed form: line 3 has the literal em-dash codepoint (3 UTF-8
bytes 342 200 224), and line 4 has the 6 ASCII characters \u2014. Now let me
read it and attempt the Edit.
Read 1 file (ctrl+o to expand)
● Now the critical test — trying to edit both price lines at once, which spans
the mixed representation:
● Update(/tmp/claude-edit-repro/recipes.ts)
⎿ Error editing file
● Yes, the bug is still reproducible. The exact error from the issue appears,
word for word.
★ Insight ─────────────────────────────────────
Why this happens: The fundamental issue is a representation gap between JSON
and file bytes.
1. JSON conflation: In JSON, \u2014 and the literal — character are the same
value. By the time the Edit tool receives old_string, it has one em-dash
codepoint — there's no way for the caller to distinguish "I mean the 6 ASCII
chars" from "I mean the codepoint."
2. Whole-string swap is all-or-nothing: The Edit tool's fallback tries two
forms — all codepoints expanded, or all escapes preserved — but when the file
has both in the same region, neither whole-string variant matches.
3. The caller can't fix it: Since JSON normalizes the escape before the tool
ever sees the parameter, no old_string value can express "line 3 has the
codepoint, line 4 has the ASCII escape."
─────────────────────────────────────────────────
Let me verify the file is unchanged and clean up.
● Bash(od -c /tmp/claude-edit-repro/recipes.ts | head -10)
⎿ 0000000 / / S a m p l e d e s s e
r
0000020 t p r i c e r a n g e s . \
… +15 lines (ctrl+o to expand)
● File unchanged, confirmed.
Summary: The bug from #52813 is still reproducible on the current version. The
Edit tool fails when a multi-line old_string spans a region where the same
Unicode character appears as both a literal codepoint and a \uXXXX source
escape. The whole-string swap fallback tries "all literals" and "all escapes"
but can't handle the mixed case. The workaround remains: use sed/node/bun via
Bash for these edits.
✻ Sautéed for 1m 28s
❯ /feedback The bug from #52813 is still reproducible on the current version. The Edit tool fails when a multi-line old_string spans a region where the same Unicode character appears as both a literal codepoint and a \uXXXX source escape. The whole-string swap fallback tries "all literals" and "all escapes" but can't handle the mixed case. The workaround remains: use sed/node/bun via Bash for these edits.
⎿ Feedback / bug report submittedThis issue has 4 comments on GitHub. Read the full discussion on GitHub ↗