Intermittent malformed tool calls: emitted as plain text instead of structured tool_use, then cascades via retry
Summary
Since 2026-05-20, Claude Code intermittently produces an assistant turn where a tool call is emitted as a plain-text block containing literal <invoke name="..."> / <parameter name="..."> markup, instead of a structured tool_use content block. Claude Code then shows Your tool call was malformed and could not be parsed. Please retry. A single occurrence frequently cascades into a run of consecutive failures.
Findings below come from analyzing local session transcripts (~/.claude/projects/**/*.jsonl): 75 malformed tool calls across 7 sessions, 2026-05-20 … 05-22.
Environment
- Claude Code 2.1.145
- Model: claude-opus-4-7 (1M context)
- Official Anthropic API, direct (no gateway / compatibility endpoint)
- Linux (WSL2)
- Reproduces in both the CLI and the VS Code extension
- Hooks: only
PreCompact/Notification/Stop(noPreToolUse/PostToolUse) - Plugins:
superpowers,episodic-memory; one MCP server (GitLab)
Symptom / signature
The broken assistant message is a single text content block whose text looks like:
count
<invoke name="Bash">
<parameter name="command">echo hello</parameter>
</invoke>
- The tool-call opening frame is missing: no
<function_calls>wrapper, and noantml:namespace prefix on<invoke>/<parameter>. - In its place is a stray word. Observed values:
call,count,court. - The stray word is consistent within a session but varies across sessions.
stop_reasonis sometimestool_use, sometimesend_turn.- Tool-agnostic: wraps Bash, Edit, Read, Task, TodoWrite, MCP calls, etc.
Intermittent + cascading
- Cannot be forced; occurs intermittently (clear quiet vs. busy periods).
- After a failure, Claude Code injects the retry message but leaves the malformed text in the conversation. The model then reproduces the broken format from its own previous turn — a self-imitation cascade (one session cascaded 24 deep).
/cleardoes not prevent it: a freshly-cleared session went malformed ~13 events in, with a short context.
Two-layer analysis
Layer A — the initial misfire (appears server / inference-side):
- The malformed
<invoke>text is recorded as the API response content; the message carries API-assignedrequestId,message.id,stop_reason, andusage. Per documented Anthropic API behavior,tool_useis delivered as a structured content block — so the malformed<invoke>text appears to have come from the API response itself. stop_reason: tool_usepaired with atextcontent block = the API determined a tool was intended but did not deliver a structuredtool_useblock.- Genuine first-misfire rate ≈ 0.5% (7 session-initial misfires / 1447
tool_useblocks). The higher gross rate is cascade.
Layer B — the cascade (actionable in Claude Code):
- The retry path keeps the literal malformed
<word>+<invoke ...>text in the next request's context. The model copies it verbatim, so a single ~0.5% misfire is amplified into a long run of failures. - Suggested investigation: whether the retry path can avoid re-injecting the literal malformed text into the next request's context — leaving it there appears to seed the cascade.
Regression boundary + correlation
- Earliest occurrence: 2026-05-20T01:34:12Z. Zero occurrences before that date.
- Before/after, by day (one project):
- 2026-05-19: 147
tool_useblocks, 0 malformed - 2026-05-20: 633
tool_useblocks, 18 malformed - 2026-05-21: 721
tool_useblocks, 55 malformed - The account's subscription plan changed on 2026-05-20 (Max 20× → Max 5×). The onset coincides exactly with the plan change. No model or config change on the user side (same model in continuous use since late April). This suggests the regression may be tied to the serving tier. (Correlation only; not verifiable from the client side.)
Ruled out (checked against transcripts)
superpowers skills — including subagent-driven-development, read in full: contains no <invoke> XML, and malformed calls also occur in sessions where that skill was never loaded; CLAUDE.md / config example syntax; hooks; episodic-memory; /clear; conversation history; model change; VS Code vs CLI; project-specific cause (the per-project log skew is pure exposure — one project had 1447 tool calls vs. others' 60 / 27).
What would help
- Layer A: please check the serving path for claude-opus-4-7 around 2026-05-20, particularly for the Max 5× tier. I have
requestIds for all 75 occurrences (regression anchor + full cascade) and can provide them to the team through a private channel on request. - Layer B: the retry-handling change above is fixable in Claude Code independent of Layer A.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗