Persistent 400 'thinking blocks in the latest assistant message cannot be modified' — interleaved thinking persisted with empty text + signature bricks sessions

Resolved 💬 5 comments Opened May 28, 2026 by CapturedMomentum Closed Jun 30, 2026

Summary

Under extended/interleaved thinking, any assistant turn that interleaves thinking with multiple tool calls gets persisted to the session JSONL with its thinking blocks emptied ("thinking": "") while the signature is retained. On the next request the API rejects the replayed (now-modified) thinking blocks with:

API Error: 400 messages.<N>.content.<M>: `thinking` or `redacted_thinking` blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.

Because every subsequent request replays the same poisoned turn, the session dies on every turn and is unrecoverable without manually editing the transcript.

Environment

  • Claude Code 2.1.154
  • Model: Opus (claude-opus-4-7), adaptive thinking with CLAUDE_EFFORT=xhigh
  • Platform: WSL2 (Linux 6.6 / Ubuntu), Node v24.14.0
  • Sessions persisted as JSONL under ~/.claude/projects/<project>/<id>.jsonl

Impact

High. A single affected turn bricks the entire session — resume reloads the same broken message and 400s again. The only recovery is hand-editing the transcript.

Reproduction

  1. Run a session with high effort / interleaved thinking on Opus.
  2. Have the assistant do a turn that interleaves thinking with several tool calls (e.g. a handful of Bash calls with reasoning between them). Reliably reproduces on a brand-new, clean session within the first assistant turn — observed crashing at messages.1.content.9.
  3. The next request fails with the 400 above; every retry/resume fails identically.

Root-cause evidence (from inspecting the JSONL)

  • All thinking blocks on disk have empty text + a signature: in one affected transcript, 909/909 thinking blocks had "thinking": "" (len 0) with a populated signature.
  • The crash is specific to interleaved thinking: turns whose thinking block sits at a non-leading position within the assistant turn (a thinking block emitted after a tool_use, all sharing one message.id / requestId). In a 1332-turn transcript, exactly the turns with such interior thinking blocks were corrupt; turns with only a single leading thinking block replay fine.
  • On disk these turns appear as many assistant entries sharing one requestId, with a block sequence like [thinking, text, tool_use, tool_use, …, thinking, tool_use, …, thinking] — the interior/trailing thinking entries are the ones flagged by content.<M>.
  • The error's content.<M> index lands on one of these interior thinking blocks; messages.<N> is the position of that turn in the replayed request.

So: the harness empties the thinking text but keeps the signature, and the API's "thinking blocks must be unmodified" constraint then rejects the interleaved blocks on replay. A single leading thinking block is tolerated; the interleaved case is where it surfaces.

Suggested fix (harness side)

When replaying prior assistant turns, either:

  • (a) preserve the original thinking text alongside the signature for blocks that must be sent back, or
  • (b) drop thinking blocks from replayed assistant messages entirely rather than sending empty-text stubs with signatures.

Emptying the text while keeping the signature is the broken middle ground.

Workaround (no config change, no loss of thinking)

Fully exit the session, then remove only the interior thinking blocks from the JSONL (keep the legitimate leading thinking block of each turn; the removed blocks are already empty so no reasoning is lost), repointing any parentUuid that referenced a removed record. After that the session resumes cleanly. Disabling thinking is not required and is not an acceptable workaround for deep-reasoning workflows.

View original on GitHub ↗

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