Intermittent malformed tool calls: emitted as plain text instead of structured tool_use, then cascades via retry

Resolved 💬 3 comments Opened May 22, 2026 by hnakamura Closed May 22, 2026

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 (no PreToolUse / 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 no antml: 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_reason is sometimes tool_use, sometimes end_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).
  • /clear does 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-assigned requestId, message.id, stop_reason, and usage. Per documented Anthropic API behavior, tool_use is delivered as a structured content block — so the malformed <invoke> text appears to have come from the API response itself.
  • stop_reason: tool_use paired with a text content block = the API determined a tool was intended but did not deliver a structured tool_use block.
  • Genuine first-misfire rate ≈ 0.5% (7 session-initial misfires / 1447 tool_use blocks). 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_use blocks, 0 malformed
  • 2026-05-20: 633 tool_use blocks, 18 malformed
  • 2026-05-21: 721 tool_use blocks, 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.

View original on GitHub ↗

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