[BUG] Opus emits tool calls as prose and mangles ASCII to typographic glyphs when preceding input has typographic punctuation (decode-time input-style contamination)

Resolved 💬 2 comments Opened Jun 13, 2026 by TKMD Closed Jun 17, 2026

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

  1. Start a Claude Code session on Opus.
  2. 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.
  3. In the same or next turn, ask the model to run a shell command or dispatch a tool/subagent.
  4. 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.
  5. 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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗