[BUG] Unicode escapes are sometimes taken literally

Status Open
Maintainer reply None cached
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

  1. create foo.py
  2. claude 'add a module-level constant to foo.py with "\ufeff" as its value'
  3. it ends up with the equivalent utf-8 bytes
  4. ask it to put an escape instead
  5. 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_

View original on GitHub ↗