Cancelling a parallel tool-call batch corrupts thinking blocks -> 400 "thinking blocks cannot be modified" permanently wedges the session

Status Open
Reported on v2.1.153
Maintainer reply None cached
Activity 8 comments · opened May 28, 2026

Problem

With extended thinking enabled, cancelling a batch of parallel tool calls (one tool errors → the rest are auto-cancelled) corrupts the thinking blocks of the in-flight assistant message. The next API request is rejected with a 400, and because the corrupted message stays in history, every subsequent turn re-sends it and re-fails — the session is permanently wedged.

Verbatim error (recurred on every turn afterward — including a plain text question and a slash-command invocation): `` API Error: 400 messages.1.content.54: thinking or redacted_thinking blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response. ``
Verbatim transcript leading up to it — a batch of parallel Bash tool calls, the first of which errored, cascading cancellations onto the rest: `` ⏺ Bash(safari-browser screenshot /tmp/plaud_search.png --url plaud 2>/dev/null && echo "saved") ⎿ Error: Exit code 1 ... ⏺ Bash(safari-browser js "1+1" --url plaud ...) ⎿ Cancelled: parallel tool call Bash(safari-browser screenshot /tmp/plaud_sea…) errored ⏺ Bash(echo "PLAIN_TEXT_TEST_NO_BROWSER") ⎿ Cancelled: parallel tool call Bash(safari-browser screenshot /tmp/plaud_sea…) errored ⎿ API Error: 400 messages.1.content.54: thinking or redacted_thinking blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response. `` — Source: Claude Code session transcript, 2026-05-29

Type

bug

Steps to reproduce (best-effort)

  1. Extended thinking enabled (default in this session)
  2. Model: claude-opus-4-7 (1M context)
  3. In one assistant turn, issue a batch of parallel tool calls (here: ~10 Bash calls)
  4. Arrange for the first call in the batch to error (here safari-browser screenshot … 2>/dev/null → Exit 1), so Claude Code auto-cancels the remaining parallel calls (Cancelled: parallel tool call … errored)
  5. On the next API request, observe the 400

Expected

Cancelling parallel tool calls MUST preserve the assistant message's thinking / redacted_thinking blocks byte-for-byte (the API requires they stay identical when thinking + tool use is active), or discard the incomplete assistant turn cleanly. Either way the conversation should remain continuable.

Actual

A thinking block (here messages.1.content.54) is modified during the cancellation/reconstruction. The API rejects the request with 400. The bad message persists in conversation history, so every subsequent turn replays the same 400 — the user cannot even ask "what happened" or invoke a slash command. Only /clear or a new session recovers.

Impact

Total, unrecoverable session loss, triggered by a routine event (one tool in a parallel batch failing). High severity for any workflow that issues parallel tool calls with extended thinking on.

Environment

  • Claude Code 2.1.153
  • Model: claude-opus-4-7 (1M context), extended thinking enabled
  • Platform: macOS (Darwin 25.5.0)

View original on GitHub ↗

8 Comments

craigts · 3 months ago

I hit this same error message via a different repro. Not sure if I should log a separate issue since it essentially is just the tool call(s) being canceled. My repro:

  1. Get to a state where claude asks you questions.
  2. Answer the questions.
  3. Hit esc to cancel the processing.
  4. Tell it to ask you the questions again.
  5. Answer the questions.

Your context is now polluted and you will receive this same error about modifying thinking blocks.

  • Claude for mac Claude 1.9255.2 (1dc8f7) 2026-05-27T01:57:20.000Z
  • Model: claude-opus-4.7 (1M context), max effort
  • Platform: macOS Tahoe 26.3 (25D125)
yurukusa · 3 months ago

Independent confirmation that this is structurally a sub-pattern of Cluster 13 (Extended-Thinking Session Wedging) rather than a standalone bug — and the parallel-tool-cancellation trigger surface for the same root failure mode documented at #63147.

Cluster framing

Tracking this as sub-pattern 13C in the four-axis cluster: extended thinking + one of four trigger conditions, all converging on 400 ... thinking blocks cannot be modified. The sub-patterns share one root failure mode (thinking-block serialization not surviving the round-trip), but reach the failure through different code paths:

  • 13A — resume serialization corruption (#63147, 33 reactions, canonical root-cause analysis by @jdrolls)
  • 13B — cancel-during-AskUserQuestion poisoning (#63143)
  • 13C — parallel-tool-batch cancellation corruption (this issue)
  • 13D — intermittent signed-thinking-block replay (#63335 + 10+ duplicate-flagged reports)

Field guide articulating all four sub-patterns: Extended-Thinking Session Wedging — A 36-Hour Surge with 4 Sub-Patterns and Operator-Side Recovery Paths (MIT, ~2,800 words).

What 13C uniquely surfaces

The cancellation path that triggers 13C is auto-cancellation (one tool errors, the harness cancels the rest) rather than the user-initiated cancellation that triggers 13B. The operator is not in the failure path — the harness pre-empts a batch in response to a sibling tool's error, and the in-flight assistant message's thinking blocks are corrupted as a side effect of the auto-cancel.

This is structurally important because it means the operator-side defense for 13C has to fire before the batch dispatches, not at the moment of cancellation. By the time Cancelled: parallel tool call <tool> errored appears, the corruption has already happened. The next turn returns the 400 and the session wedges.

@craigts's variant repro (different code path, same end state) reinforces the framing: the trigger is "any cancellation event on an in-flight thinking-block-bearing assistant message during a parallel batch," not specifically the auto-cancel-on-sibling-error path. The serialization defect is upstream of which kind of cancellation fires.

Operator-side defenses (cc-safe-setup)

The hook shipped 2026-05-29 (PR #445, 54 tests passing) targets sub-pattern 13A at SessionStart only — it won't catch 13C, since 13C fires mid-session when a parallel batch lands.

The 13C advisory is in design: a PreToolUse hook on Bash batches that, when extended thinking is enabled and a parallel batch of 3+ tool calls is about to dispatch, surfaces a one-line stderr advisory recommending sequential execution instead. Rate-limited to one advisory per session. The cost of serializing 3 tool calls (a few seconds of additional latency) is small relative to the cost of a permanently wedged session. Targeting a June 2026 ship.

Operator-side workaround in the meantime: when working in an extended-thinking session, dispatch tool calls sequentially rather than in parallel batches. The output is the same; the failure surface is narrower.

For sessions that have already wedged via this trigger, the post-hoc transcript repair tool miteshashar/claude-code-thinking-blocks-fix is the recommended entry point — it operates on the on-disk transcript and produces a repaired version that can resume cleanly. The cluster's recovery surface is structurally narrow (hooks cannot reach the streaming-response handler where the corruption happens), so this tool is genuinely the best move when you're currently in a wedged session.

What I'm watching for

The 13C trigger surface is specifically "auto-cancellation of parallel tool calls when one errors." That cancellation handler is presumably shared with other parallel-execution paths (MCP tool batches, subagent tool batches), so I expect to see structurally similar reports surface for those code paths as Cluster 13 gets more attention. I'm tracking the trigger-surface refinement in the cluster registry: cluster-tracker.html#cluster-extended-thinking-wedge.

Benjamin-Sterrett · 3 months ago

Confirming 13C on Opus 4.8 (1M context) with two new trigger surfaces beyond the safari-browser repro — both are auto-cancellation of an in-flight parallel batch, exactly as framed above.

Variant 1 — hung hook probes during a CLI workflow's startup. A dev-workflow orchestrator opened with a few hook-infrastructure probes (sourcing a shared shell helper, a writability check). Several returned no result (hung), so — impatiently — I dispatched another parallel batch mixing more Bash probes with several TaskCreate calls. One Bash (cd "$HOME/.claude/hooks" …) errored and cascaded Cancelled: parallel tool call … onto the whole batch, including the non-Bash TaskCreate calls. Next turn:

API Error: 400 messages.7.content.3: `thinking` or `redacted_thinking` blocks in the latest assistant message cannot be modified.

Session wedged; every subsequent turn (incl. /exit attempts) replayed the 400. Notable: the cancelled batch was mixed tool types (Bash + TaskCreate), so the corruption is not Bash-specific — it's the parallel-batch cancellation path itself, consistent with @yurukusa's "upstream of which cancellation fires" point.

Variant 2 — blocking sleep poll cancelled. Separately, a Bash(sleep 120; gh pr view …) CI-poll running in a parallel batch was cancelled and produced the same 400 (×6 in a single session). Anything long-lived in the batch (a sleep, a hung probe, a run_in_background completing) widens the window where a sibling error can cancel mid-flight.

Fleet-onset data point. Across three of my sessions the 400 first appears 2026-05-28 (×6 that day), then 05-29 (×1), 05-30 (×1) — and nearly every issue in this cluster was filed 2026-05-28. All of mine were already on claude-opus-4-8, which lines up with #63412 ("Opus 4.7 unaffected"): the 4.8 rollout appears to be when signed thinking blocks became fragile under cancellation. alwaysThinkingEnabled: true is the amplifier (guarantees every turn carries blocks to corrupt), not the cause.

Operator mitigation that's holding for us (matches the "defense must fire before dispatch" framing): in extended-thinking sessions, never run blocking sleep/poll loops in Bash (use background execution and poll one task at a time), and never dispatch a parallel batch you might cancel — serialize anything that can error. Same output, much narrower failure surface.

  • Claude Code (CLI), claude-opus-4-8 1M context, extended thinking on
  • Platform: macOS (Darwin 25.5.0)
yurukusa · 3 months ago

@Benjamin-Sterrett — thank you for this. Both variants are exactly the kind of confirmation that closes the loop on 13C's mechanism, not just its existence.
The mixed-tool-type cancellation (Bash + TaskCreate) is the decisive piece. Until your Variant 1, the public repros were Bash-flavored (the safari-browser minified output case, my earlier sleep-loop case), which left open whether the corruption was tool-specific — e.g. a streaming-output edge case in Bash's pty handling that didn't survive the round-trip. Your case kills that hypothesis: a Bash(cd …) error cancelling sibling TaskCreate calls produces the same 400. That means the corruption sits squarely on the parallel-batch cancellation path itself, upstream of any per-tool serialization. The serializer is preserving block signatures across the cancellation, but the block content fragments are no longer the bytes those signatures were computed over → next turn, validator rejects.
Variant 2 (Bash(sleep 120; …)) is the latency amplifier I hadn't framed clearly. Anything that widens the window between dispatch and all-complete increases the probability a sibling errors mid-flight. That's why CI poll loops, hung hook probes, and run_in_background completions all show up disproportionately in 13C repros vs. snappy parallel batches. Worth pulling into the field guide as a separate "trigger amplifier" axis — the cancellation path is the root cause, but the time-in-flight of the batch is the exposure surface.
Fleet-onset 2026-05-28 (×6 that day) on claude-opus-4-8 ↑ #63412. This corroborates the version-inflection hypothesis that came out of #63412's "Opus 4.7 unaffected" thread: signed thinking blocks became fragile under cancellation specifically in the 4.8 rollout. Combined with the 13B / 13C / 13D filings all clustering on 2026-05-28, this is now strong enough to drop "intermittent" from the description of 13D — the variance there is amount-of-exposure, not random.
I'm folding your mitigations into the field guide as the 13C explicit operator-side recovery surface (the cluster's defense had to fire pre-dispatch, exactly as you framed it):

  1. Serialize anything cancellable in extended-thinking sessions. The parallel-batch primitive is the failure surface; don't dispatch a batch you might cancel.
  2. No blocking polls in Bash during extended thinking. run_in_background + single-task polling has the same throughput with a much narrower failure window.
  3. Avoid mixing tool types in a cancellable batch. When you can't avoid it, prefer a single-tool batch first and only dispatch the heterogeneous batch once you have results.

The third one is the new addition from your Variant 1 — I had this only as folklore from safari-browser reproductions. Your data makes it explicit.
I'm treating 13C as structurally resolved at the operator level now (defense fires pre-dispatch, recovery path is /exit + serialization discipline). The remaining ambiguity sits in 13D (intermittent signed-block replay) where the trigger surface is still less crisp — if your Variant 2's blocking-poll cancellation produced 6 of the day's 400s on its own, that may be more of 13D's volume than the "intermittent" label suggests.
Two follow-up questions if you have a minute:

  1. In Variant 1, did the cancelled TaskCreate calls have any thinking blocks attached to them in the transcript, or was the corrupted block tied to the cancelled Bash? (i.e. was the block the cancellation interrupted, or a sibling block elsewhere in the assistant message?)
  2. Across the three sessions with the 2026-05-28 onset, are any of them on Claude Desktop / VS Code rather than CLI? The 13B reports skew toward Desktop (#63143), and I'd like to know whether 13C is CLI-exclusive or surface-agnostic on Opus 4.8.

Either answer narrows the failure model further. Thank you again for taking the time to write this up at the level of detail you did — this is exactly the data that moves the cluster from "anecdote pile" to "characterized failure mode".

RyanAlberts · 3 months ago

Confirming this still reproduces on Claude Code 2.1.158 (macOS) — the wedged transcript in our case was on 2.1.147, so it spans at least 2.1.147 → 2.1.158, not yet fixed.

Identical mechanism to the repro above: a batch of parallel Bash tool calls, the first errored, the rest auto-cancelled (Cancelled: parallel tool call Bash(...) errored), then:

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

Same permanent-wedge behavior: every subsequent turn (including a plain text question and a slash-command) re-sent the corrupted assistant message and re-failed. Only fix was to abandon the session and start fresh — --resume re-fails immediately. Running tool calls sequentially or disabling extended thinking avoids re-triggering it.

yurukusa · 3 months ago

@kiki830621 — your transcript is the cleanest articulation I have seen of how two structural failure clusters compose into a single unrecoverable wedge. I have been tracking these as independent clusters; your reproduction is the bridge I was missing.
Cluster 20 — Parallel tool batch cancellation cascade — was articulated 2026-05-31 from three independent same-day reports (#64059 / #64052 / #64047). Failure mode: any single non-fatal tool error cancels every sibling call in the parallel batch with Cancelled: parallel tool call X errored. Two axes — the cascade behavior itself, and the cancellation message reading identically to a user interrupt (so the model misattributes the cascade to user action).
Cluster 13 — Extended Thinking wedge — has been accumulating since mid-May. The lead issue is #63147 (40 reactions) and at least 10 duplicate-or-related filings within a 72-hour window from 2026-05-28: #63072, #63078, #63121, #63199, #63231, #63239, #63335, #63337, #63341, #63346, #63463. Failure mode: 400 messages.N.content.M: thinking or redacted_thinking blocks in the latest assistant message cannot be modified, every subsequent turn re-fails because the corrupted message stays in history.
Your issue is the cross-cluster bridge: the cluster 20 cascade is one of the things that corrupts the thinking blocks that then trigger the cluster 13 wedge. Until now I had read these as two independent failure modes; your transcript shows them composing.
Cluster 20 in isolation wastes tokens (40-100K per cascade event, ~$1.50-$7.50 at Opus output rates) but the session can usually continue — the operator just sees the cascade and adjusts.
Cluster 13 in isolation has a known operator workaround: /clear to discard the corrupted session, accepting the context loss.
The composition is unrecoverable in a different way. When the cascade event itself causes the thinking-block corruption, the operator hits the cluster 13 wedge with no prior signal that the cluster 20 cascade was the trigger. The natural debugging response — "why is my session wedged? I will look at the last few turns" — does not surface the cascade as the cause, because the cascade is in a previous turn that completed normally except for the silent corruption. Operators end up either re-clearing repeatedly (and learning to fear extended thinking + parallel calls) or blaming the wrong subsystem.
Stitching the cluster-20 mechanics into the cluster-13 corruption signature from #63147:

  1. Assistant turn emits thinking blocks + a parallel batch of tool calls
  2. Tool call 1 errors (exit 1 in your case)
  3. Cluster 20 cascade cancels tool calls 2..N
  4. The assistant message is persisted with thinking blocks that have empty thinking text but retained signature (the on-disk shape jdrolls documented in #63147)
  5. Next API request re-sends the corrupted message
  6. Server rejects with 400 thinking/redacted_thinking blocks cannot be modified
  7. Every subsequent turn re-sends the same corrupted history → permanent wedge

The cluster 20 cascade is one corruption trigger; cluster 13 documents at least one other (session resumption). Both produce the same on-disk shape because both interrupt the thinking-block lifecycle at the same point.
For operators running extended thinking with parallel tool calls, the cluster 20 mitigations are necessary but not sufficient — they reduce cascade frequency, which reduces this wedge's frequency, but a single cascade with extended thinking enabled is enough to wedge a session permanently.

  1. Cap parallel batch size at N=3-5 via CLAUDE.md guidance — same as the cluster 20 mitigation. Smaller batches = smaller cascade radius = lower probability of hitting the corruption point with extended thinking active.
  2. Avoid git / curl / pkill (the three most-cited cluster 20 cascade triggers) in parallel batches when extended thinking is enabled — even one mistake there can wedge the session for the rest of the day.
  3. Pre-flight session backup before high-risk turns — the cluster 13 wedge is unrecoverable from the corrupted transcript, but the conversation state up to the wedge is preserved if you keep a pre-turn snapshot of the .jsonl.
  4. Workaround for an already-wedged session — the proposed fix path in #63147 (edit the .jsonl to re-shape the corrupted block) is operator-side but invasive. The conservative path is /clear and accept context loss.

Two cluster-20 defense hooks shipped 2026-05-31 in cc-safe-setup (MIT, ~1,500 unique clones / 14 days):

  • parallel-cascade-detector.sh (PR #501, PostToolUse) — counts Cancelled: parallel tool call X errored in a rolling window and warns when threshold crossed. Surfaces the cascade after it has happened.
  • parallel-batch-size-limiter.sh (PR #503, PreToolUse) — counts parallel tool calls in a 500ms rolling window and warns at batch-size threshold. Surfaces the size signal before any failure can cascade.

Neither hook prevents the cluster 13 corruption — they reduce cluster 20 cascade frequency. The cluster 13 wedge is an upstream-side fix: the server needs to either (a) reject empty-text-but-signed thinking blocks from being persisted in the first place, or (b) tolerate them on re-send rather than rejecting with 400.
For cluster 20 (cascade behavior): change the default from "fan-out, abort on first failure" to "fan-out, continue on partial failure". The right semantic for parallel reads/probes; the existing semantic optimizes for transactional batches, which are the rare case.
For cluster 20 axis 2 (indistinguishability): distinguish system cancellation from user cancellation in the result message (the minimum fix proposed in #64047).
For cluster 13 (thinking block wedge): persistence-layer change — either reject the corrupted shape at write time, or tolerate it at re-send time. The current behavior persists invalid-on-read state, making every subsequent turn re-fail.

Your transcript is going into my cross-cluster reference set; it is the cleanest articulation I have for the cluster 20 → cluster 13 composition. Filing your report at the time you did (rather than waiting for a separate model-behavior issue) was the right call — the composition is the actionable insight.

redbox88 · 3 months ago

**Same root cause on Linux, triggered by a user-injected prompt mid-batch (not a tool-error cascade).**

Env: Claude Code 2.1.122 embedded in Claude Desktop 1.9255.2, official API (1p), Linux.

In my session the corruption was triggered not by a tool erroring, but by sending a normal message while a parallel tool batch + interleaved thinking was still in flight. Transcript event order:

assistant: thinking → tool_use → thinking → tool_use → tool_use   (parallel batch, still running)
<user submits a new prompt here>           ← interrupt mid-execution
user: tool_result ×4                        (results arrive ~13s later)
API Error: 400 messages.17.content.5: `thinking` or `redacted_thinking` blocks in the
latest assistant message cannot be modified. These blocks must remain as they were in
the original response.

Every retry afterward (continue, a new prompt, even asking about the error) re-sent the frozen turn and re-failed with the identical 400 → session permanently wedged; only a fresh session recovered.

So the trigger generalizes beyond auto-cancel-on-error: any interruption of an in-flight interleaved-thinking + parallel-tool assistant turn corrupts content.M and wedges the session — here it was just a normal user prompt arriving while the parallel tool batch was still executing.

gomes0202 · 3 months ago

A mesma coisa acontece excessivamente comigo. EXCESSIVAMENTE.