[BUG] Unicode escapes are sometimes taken literally
Status Open
Maintainer reply None cached
Workaround ✓ Mentioned in description ↑
Activity 0 comments · opened Aug 21, 2026
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When I try to tell claude to add a module-level global with "\ufeff" in it, it puts the literal. I've found basically no way to say "use the unicode escape for nonprinting characters always", even in a memory. It stores the text I give it, which gets interpreted by some tools.
I tried to share session 959fd0d9-0edc-4538-8d8c-a48ac50e1445 in case that's helpful.
What Should Happen?
It should write
BOM = "\ufeff"
which is what I told it to do.
Error Messages/Logs
Summary: Edit/Write/Bash tool-call arguments are transmitted as JSON. When the model writes \uFEFF (or any \uXXXX) inside a JSON string argument intending to produce literal escape text in the target file, the JSON parser decodes it into the raw Unicode code point before it reaches the file — there's no way for the tool call to distinguish "I meant this as text" from "I meant this as an escape." The workaround is to double the backslash (\\uFEFF) so JSON decodes it to the literal two-character text \ + uFEFF.
Why it's easy to miss: repr() on the resulting Python string can't tell the difference either — a source file containing a raw U+FEFF character and one containing the six-character text \ufeff produce identical string objects once read, so a repr-based sanity check gives false confidence. Only inspecting actual bytes (e.g. grep -c $'\xef\xbb\xbf' file) reveals which one is actually on disk.
Impact: This bit twice in one session while implementing a BOM-handling fix and writing a regression test — both times a \ufeff intended as literal escape text silently became a raw BOM byte in the source file.
Suggestion: Document this JSON-escaping behavior explicitly in the Edit/Write tool descriptions, since it's a natural mistake whenever nonprinting/control characters need to appear as escape sequences in generated code.
Steps to Reproduce
- create foo.py
- claude 'add a module-level constant to foo.py with "\ufeff" as its value'
- it ends up with the equivalent utf-8 bytes
- ask it to put an escape instead
- it insists it already is
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
?
Platform
Other
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_