Opus 4.8 corrupts tool-call control tokens (function_calls opener becomes a plain word, invoke loses the antml: namespace) — output leaks and the session stalls

Open 💬 0 comments Opened Jun 28, 2026 by toa01728

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?

During tool use, Opus 4.8 intermittently emits malformed control tokens instead of valid function-call markup:

  • The function_calls opener is replaced by a plain word that varies each time (observed: "court", "count", etc.).
  • The invoke / closing invoke tags lose the antml: namespace and come out as bare tags.

The harness only accepts namespaced tokens, so it rejects the call, the raw fragment leaks into the visible output, and the system injects: "Your tool call was malformed and could not be parsed. Please retry." Not observed on Sonnet 4.6.

What Should Happen?

Tool calls should always be emitted with valid namespaced control tokens so the harness can parse them on the first try. The model should never emit a renamed opener or bare (non-namespaced) invoke tags into the visible output.

Error Messages/Logs

Injected by the harness on each occurrence:
"Your tool call was malformed and could not be parsed. Please retry."
Exact log signature of a genuine event:
"role":"user","content":"Your tool call was malformed and could not be parsed. Please retry."},"isMeta":true

Steps to Reproduce

This is intermittent (no 100% deterministic repro yet), but the conditions that trigger it for us:

  1. Use Opus 4.8 (1M context) in Claude Code.
  2. Work in a context with Japanese (CJK) text immediately before a tool call.
  3. Issue tool calls (it also happens inside subagents).

Result: occasionally the function_calls opener comes out as a plain word and invoke tags lose the antml: namespace; the fragment leaks to screen and the retry is injected.
Frequency from our session logs (19 genuine cases): 06-22:1 / 06-24:1 / 06-25-26:0 / 06-27:4 / 06-28:13 — i.e. ~0 then a sharp spike on 06-27.
A single occurrence usually self-recovers on the next retry, BUT consecutive corruptions do NOT: the retry reproduces the same corruption and the session stalls repeatedly until the user presses Esc and rephrases (reproducible real case: a repeated sync tool call stalling 3 times in a row).

Claude Model

Opus

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.187 (same CLI version worked fine before 06-27 — the binary did not change since 06-24, so we suspect a server-side change rather than a client update)

Claude Code Version

2.1.187 (Claude Code)

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

How we counted (for your log-side reproduction):
Count only genuine harness-injected events by matching the exact meta record in ~/.claude/projects:
"role":"user","content":"Your tool call was malformed and could not be parsed. Please retry."},"isMeta":true
A loose search for "could not be parsed" yields false positives (it matches discussion text in the transcript). The model's corrupted raw output is the record immediately preceding that malformed-meta record.
Clients: npm CLI (v2.1.187, binary unchanged since 06-24) and desktop app (updated 06-27). Both share ~/.claude/projects, so logs cannot pin the offending client.
What we'd like:

  1. Whether a recent server-side change to Opus 4.8 token generation around 06-27 could explain the spike.
  2. Whether the namespace-loss can be hardened harness-side (reject-and-retry currently loops on the same corruption instead of recovering).

Note: our environment contains confidential data, so we are not sharing raw transcripts; the signature and statistics above should be enough to locate it. Happy to answer follow-ups.
Workarounds we found: switching to Sonnet 4.6 eliminates recurrence; keeping sessions short / starting clean helps (corrupted examples in context get imitated as plain text — a secondary context-pollution failure mode).

View original on GitHub ↗