[BUG] Opus emits tool calls as prose and mangles ASCII to typographic glyphs when preceding input has typographic punctuation (decode-time input-style contamination)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet (closest match is #49747, which is related but a distinct signature — see "Additional Information")
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (2.1.176)
What's Wrong?
On Opus, the model intermittently corrupts its own outputs in two ways that appear to share a decode-level root cause:
Symptom 1 — tool calls rendered as prose (silent no-op). A tool/function invocation is emitted as literal prose in the body text (stray <invoke ...> markup or bare fragments) instead of a structured tool call, so no tool call is dispatched. There is no error, no execution, and no tool result — the step silently does nothing.
Symptom 2 — ASCII mangled into typographic glyphs. ASCII inside generated shell commands is replaced by typographic characters: git branch --show-current becomes git branch –show-current (en-dash), and echo "===" becomes echo "===" (curly quotes). The resulting commands are non-executable.
Triggers (two observed, likely the same underlying instability):
- (a) Long input containing typographic punctuation. When the immediately-preceding user turn is a long block whose punctuation has been converted to typographic glyphs (curly quotes
"", en-dashes–, bullets•) — e.g. text round-tripped through a rich-text/markdown-rendering layer — the next outputs corrupt with BOTH symptoms. The smoking gun: the glyphs the model emits match the exact punctuation style of that input (--→ the same en-dash, straight quotes → the same curly quotes). This is decode-time style-bleed / input-distribution contamination.
- (b) Long preceding context generally (no typographic input required). Symptom 1 (tool-call prose-ization) was also observed immediately after a long non-typographic tool result / system notification (hundreds of lines), with no typographic user input in between. This suggests the prose-ization correlates with the length of preceding context, independent of typographic glyphs; typographic input (trigger a) additionally adds Symptom 2.
The effect persists across turns while the long/contaminated content remains in the context window, and decays once it scrolls back (later short / plain-ASCII turns execute tool calls correctly). Short, plain-ASCII turns with little preceding context do not trigger it.
It is not prompt-overridable and not caused by local hooks (a session HTTP hook returns only {} / permission decisions and injects nothing into the prompt; hooks cannot alter decode-layer output).
What Should Happen?
The model should emit well-formed structured tool calls regardless of (a) the punctuation style of preceding input or (b) the length of preceding context, and should always produce straight ASCII (--, ") inside shell command strings. Typographic characters present in pasted prose must not bleed into generated code, and long preceding context must not suppress tool-call emission.
Error Messages/Logs
No error is surfaced — this is the core hazard. Corrupted tool calls are rendered as prose and silently no-op; there is no log line indicating a tool call was dropped. A mangled shell command would only fail with a shell syntax error if it were actually executed, but in the prose-ized case nothing runs at all.
Steps to Reproduce
- Start a Claude Code session on Opus.
- Either (a) paste a long multi-paragraph message whose punctuation has been converted to typographic glyphs (bullets
•, curly quotes"",--shown as–); or (b) get a long (hundreds of lines) tool result / notification into the recent context. - In the same or next turn, ask the model to run a shell command or dispatch a tool/subagent.
- Observe: the tool call is rendered as prose (not executed), and/or (under trigger a) any shell command it writes contains en-dashes / curly quotes and would not run.
- Contrast: send a short plain-ASCII message ("continue") with little preceding context — the next tool call executes normally. The corruption tracks the presence of the long/typographic content in recent context, not the requested action.
Claude Model
Opus
Is this a regression?
Not sure — I cannot identify a confirmed last-working version for this specific signature.
Last Working Version
Unknown.
Claude Code Version
2.1.176 (Claude Code)
Platform
Claude API (Anthropic first-party) — via a claude.ai subscription / OAuth login in Claude Code (oauthAccount present, active subscription), not a standalone API key, AWS Bedrock, or Google Vertex AI.
Operating System
Linux (Ubuntu 22.04.5 LTS)
Terminal/Shell
bash
Additional Information
Possibly related: #49747 ("Opus 4.7 mixes legacy XML tool-use format into JSON tool calls on longer payloads"). All three may share a root cause — decode-level tool-call instability that correlates with length and is not prompt-overridable — but the axes differ:
| | #49747 | This report — trigger (a) | This report — trigger (b) |
|---|---|---|---|
| Corruption | XML structure leaks into a JSON tool call (call attempted, schema fails, retries) | Tool call fully prose-ized (silent no-op) + --→–, "→" glyph bleed | Tool call fully prose-ized (silent no-op) |
| Length axis | long tool-call output (verbose args) | long typographic input | long preceding context generally |
Safety note: because corrupted tool calls fail silently, an autonomous / agentic workflow can appear to proceed while a step is skipped — the operator cannot tell work was dropped without inspecting raw output. Observed workaround: keep recent context short and plain-ASCII; resume sessions with short messages rather than long pasted briefs.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗