[Bug] Edit tool corrupts Unicode typographic characters in non-ASCII text

Status Closed — not planned
Reported on v2.0.76
Maintainer reply None cached
Activity 14 comments · opened Dec 31, 2025 · closed Apr 3, 2026

Bug Description
Subject: Critical Issue - Claude Code Corrupts French Typography (Apostrophes) Even When Copy-Pasting

Problem:
Claude Code systematically corrupts French typographic apostrophes ' (U+2019 RIGHT SINGLE QUOTATION MARK) by replacing them with straight apostrophes ' (U+0027 APOSTROPHE) when using the Edit tool. This happens even when explicitly copy-pasting text from the Read tool.

Critical Detail:
The correct characters are already in the user's files. It's Claude Code that removes them and replaces them with the wrong characters during the Edit operation.

Impact:

  • Every single Edit operation on French code corrupts the typography
  • Makes code reviews extremely painful - developers must manually check every French string for corruption
  • Breaks professional French typography standards (U+2019 is the correct apostrophe in French)
  • Slows down development significantly - every commit requires manual verification and correction
  • Creates frustration and makes Claude Code unreliable for non-English codebases

Examples:
"Tourner l’image vers la gauche" turn into "Tourner l'image vers la gauche" and that is not desirable.

Attempted Workaround - FAILED:
We tried having Claude:

  1. Use Read tool to see the original text with correct apostrophes
  2. Copy-paste the exact text (including French strings) into Edit tool
  3. Only modify the code logic, not the French strings

Result: The workaround doesn't work. Even when Claude copy-pastes text from Read into Edit, the Edit tool call automatically converts ’ (U+2019) to ' (U+0027) before sending it to the system.

Root Cause:
The Edit tool appears to normalize/sanitize text input, converting Unicode typographic characters to ASCII equivalents. This is destructive for languages that use proper typography.

Reproduction:

  1. Create a file with French text: altinaeTooltip="Tourner l'image vers la gauche" (note the ' is U+2019)
  2. Use Read tool - Claude sees the correct apostrophe
  3. Copy-paste that exact line into Edit tool to change nearby code: [disabled]="!loaded" → [disabled]="!loaded()"
  4. Result: The apostrophe in l'image is silently converted from U+2019 to U+0027

Suggested Solutions:

Option 1 (Preferred): Add a setting/flag to preserve Unicode characters in Edit operations:

  • preserveUnicode: true - Never normalize typographic characters
  • Should be opt-in or per-workspace configurable

Option 2: Detect quoted strings and preserve their exact Unicode characters:

  • Don't normalize text inside "..." or '...' strings
  • Only normalize code/whitespace

Option 3: Provide character-level diff information to user:

  • Show when Edit would change Unicode characters
  • Let user approve/reject character normalization

Affected Languages:
This likely affects all languages with proper typography:

  • French: ' (apostrophe), «» (guillemets)

Not tested :

  • Spanish: ¿¡
  • German: „" (quotes)
  • And many others

Current Status:
We've had to document this as "Rule #1" in our codebase guidelines, but Claude Code cannot follow the rule due to this technical limitation. The workaround is impossible to implement.

Frequency: This happens on literally every Edit operation involving French text (dozens of times per day in active development).

Environment Info

  • Platform: darwin
  • Terminal: iTerm.app
  • Version: 2.0.76
  • Feedback ID:

View original on GitHub ↗

14 Comments

github-actions[bot] · 7 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

rsslldnphy · 6 months ago

This is still happening for me – not just French, but any text with proper curly quotation marks.

Rafarel · 6 months ago

In the meantime I made a script to hunt quotes with a --fix option so that any time claude make this mistake (it happens all day long) it calls the script. This is the only workaround I have for now

rsslldnphy · 6 months ago

i now have this in my CLAUDE.md lol

**If you accidentally replace curly quotes with straight quotes**, use Python
with unicode escapes to fix it. For example, to fix the string "you'd":

python3 -c "
with open('path/to/file.js', 'r') as f:
    content = f.read()
content = content.replace(\"you'd\", \"you\u2019d\")  # \u2019 is the curly apostrophe
with open('path/to/file.js', 'w') as f:
    f.write(content)
"

Note: The `sed` tool and the Edit tool struggle with curly quotes because
they get converted to straight quotes in transit.
Rafarel · 6 months ago

Here is my script that targets french langage patterns for the apostrophe.
The script searches accross all your codebase, ignoring the gitignored files.
It has a --fix option to auto fix quotes.
Pretty sure you can adapt the script to any language.
i told claude to use the script when he struggles with a quote.
quote-hunter.ts

thejud · 6 months ago

Adding two failure modes involving escape sequences, beyond the direct Unicode→ASCII downgrade described above:

\uXXXX escape sequences in new_string are written as literal text:

python3 -c "
with open('/tmp/unicode-test.md', 'w', encoding='utf-8') as f:
    f.write('She said, \u201cHello.\u201d\n')
"

Then Edit with new_string='She said, \u201cHi there.\u201d' — the file receives the literal characters \u201c and \u201d (6 ASCII chars each), not the Unicode code points. So escape sequences don't provide a workaround.

\uXXXX in old_string doesn't match actual Unicode:

old_string='She said, \u201cHello.\u201d'

Returns "String to replace not found in file." The escape sequence is treated as literal text, so the model can't match existing Unicode characters via this route either.

Result: Both the direct-Unicode path (downgraded to ASCII) and the escape-sequence path (literal text / no match) fail. There is no route through the Edit tool that preserves typographic quotes.

Mixed-encoding note: When a region is edited, the affected lines get ASCII quotes while untouched lines retain Unicode — leaving the file with inconsistent quote styles, which is arguably worse than uniform corruption.

Context: This affects prose/markdown files, not just code with French strings. Any fiction or documentation workflow that depends on typographic quotes hits this on every edit. Still present in Claude Code 2.1.45 on macOS (Darwin 25.2.0).

thejud · 6 months ago

Workaround: A pre-Edit hook that detects Unicode in the target file and blocks the Edit tool, plus a drop-in replacement script (uedit) that reads JSON edits from stdin where \uXXXX escapes are properly decoded by Python's json.loads.

Gist with both files and setup instructions: https://gist.github.com/thejud/802c31016698f1d78dbcecbde2b5575a

The hook adds a CLAUDE.md instruction telling the agent to use uedit instead, so it redirects automatically. Not a fix, but it's been reliable for daily use on a prose-heavy project.

thejud · 6 months ago

Can confirm the fix in v2.1.47 works. Tested with a file containing French characters (é, è, ç, «, »), curly quotes (" " ' '), curly apostrophes ('), and em dashes (—). All survived an Edit round-trip with correct UTF-8 bytes verified via xxd.

Retiring my workaround hook from the gist above — leaving the files in place in case of regression.

aveao · 5 months ago

I think this is a more general issue with the models or a step applied for cleanup after the raw model output before returned from serverside.

Using the chat UI to ask "What apostrophes exist in unicode?" gets it to list them, with U+2019 replaced with U+0027 (verified by checking codepoint).

Tested both Sonnet 4.6 and Opus 4.6.

I also had this break code before, by changing a .replace("[U+2019]", "[U+0027]") to .replace("[U+0027]", "[U+0027]"), making it a nop.

ArtKoKo · 5 months ago

Your description is spot on — the correct characters are already in the files, and Claude Code’s Edit operation actively destroys them. This is especially painful for French (every apostrophe is affected) but impacts any language using typographic marks.

I’ve filed a feature request proposing a /curly option: instead of blind normalization, a lightweight agent verifies whether each curly character is a code delimiter (fix it) or textual content (preserve it). This would solve both the original bug (#29786, curly in code) and the reverse problem you describe.

[FEATURE] /curly option: context-aware curly/straight character handling in Write/Edit#31482

aveao · 5 months ago

I don't think your feature request makes sense as this isn't a claude code bug, but a more general claude one. I don't think such a toggle would make sense either.

(also, please consider writing your own comments)

ArtKoKo · 5 months ago

Incredible, a human!

No, my proposal isn't just a button, but an agent (AI or otherwise) that can be activated, specialized in detecting curved characters, and therefore capable of correcting Claude Code.

Furthermore, I offer explanations that I wish I had received from Anthropic, and I also offer skills for those who are interested.

Regarding the automation of my messages by AI, this stems from the numerous issues I've opened as a human, to which the only response I've received is a bot that automatically closes the conversations.

github-actions[bot] · 4 months ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

github-actions[bot] · 4 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.