[BUG] API Error 400 - Thinking Block Modification Error
Open 💬 100 comments Opened Oct 23, 2025 by WamBamBoozle
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?
The Claude Code session became completely unresponsive with repeated API 400 errors. Every subsequent user input triggered the same error, making recovery impossible.
Symptom: Session entered an error loop where the conversation history reached a corrupted state:
- A previous assistant response contained thinking blocks
- The system attempted to modify or replay that message's thinking blocks
- The API correctly rejected this (thinking blocks are immutable once sent)
- The error persisted because the corrupted state remained in conversation history
Error Pattern: The error occurred at messages.71.content.8 (71st message, 8th content block), indicating:
- Deep conversation history (71 messages)
- Specific content block contained thinking blocks that couldn't be modified
- Every subsequent user input triggered the same error
Contributing Factors:
- Long session: Nearly 15k lines of transcript suggests extended conversation
- Complex workflow: Multiple tool calls, file reads, git operations
- Repeated questions: Same question asked multiple times may have confused state
- Message editing attempt: The system may have tried to edit/retry previous responses
Impact:
- Session Status: Completely unrecoverable
- User Action: Had to save transcript and start new session
- Work Status: Context lost mid-conversation about a technical topic
- Data Loss: Any uncommitted mental state/context
What Was Lost:
- Active conversation context and unresolved questions
- Current working state and recent discussion topics
- Mental model of the problem being discussed
What Should Happen?
Expected Behavior:
- Claude Code should handle long conversations gracefully without entering unrecoverable error states
- If thinking blocks cannot be modified (as per API constraint), the system should detect this condition before submitting the request
- When errors occur, the system should offer graceful degradation options
Ideal Recovery Flow:
- Detect when conversation state is approaching problematic conditions
- Offer to summarize current state
- Save context to file automatically
- Start fresh session with context restored
- Or provide mechanism to "rewind" conversation history to before corrupted state
Preventive Measures That Should Exist:
- Conversation state validation to detect when thinking blocks are being modified
- Message history pruning or summarization for very long conversations
- Threshold warnings when conversation history reaches risky size (>10k lines)
- Checkpoint mechanism to enable rollback to stable state
Error Messages/Logs
### Primary Error Message
API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"messages.71.content.8: `thinking` or `redacted_thinking` blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response."},"request_id":"req_011CUQZ6ttKtJbU8JQRYyizo"}
### Error Repetition Pattern
Line 14957: User asks question → API Error 400
Line 14961: User asks same question → API Error 400 (same request)
Line 14965: User asks same question → API Error 400 (same request)
Line 14969: User references the error itself → API Error 400
Line 14974: User asks to update memory files → API Error 400
### Technical Details
**API Endpoint**: Anthropic Claude API (claude.code integration)
**Error Type**: `invalid_request_error`
**Error Message Pattern**:
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.
Where:
- `{N}` = Message index in conversation history (71 in this case)
- `{M}` = Content block index within that message (8 in this case)
**Request IDs**:
- req_011CUQZ6ttKtJbU8JQRYyizo
- req_011CUQZ8XPZgRdbaXt8GJrxy
- req_011CUQZ9SAKKbLfL1g7ieqU8
- req_011CUQZAR1eH1AotrvNzGhx8
**Transcript Available**: Yes (763.4KB, 14,976 lines)
Steps to Reproduce
Context Leading to Error
- User and assistant engaged in extended session about a technical topic (~14,976 lines)
- Conversation involved multiple rounds of investigation and explanation
- Assistant provided detailed technical explanations
- User asked follow-up question requesting clarification
- Same question was asked 3 times in succession (possibly due to user not seeing response)
- ERROR STATE TRIGGERED
Reproduction Steps
- Start Claude Code session
- Engage in extended conversation with multiple tool calls, file reads, and complex workflows
- Reach approximately 71 messages deep in conversation history
- Have assistant generate response with thinking blocks
- Ask follow-up question that may trigger message retry or state replay
- Observe API Error 400 with thinking block modification error
- Attempt to continue session → error repeats for all subsequent inputs
Conditions That May Trigger
- Very long sessions (>10k lines of transcript)
- Conversation history at ~71 messages or deeper
- Assistant responses containing thinking blocks
- Rapid repeated questions (asked 3 times in succession in this case)
- Complex multi-turn workflows with many tool calls
Warning Signs
- Session approaching 10k+ lines of transcript
- Repeated API errors occurring
- Same question triggering errors multiple times
- Any API 400 error mentioning thinking blocks
Recovery Actions Taken
- User saved full transcript to local file
- User started new Claude Code session
- User asked new session to review the transcript
- User requested this bug report be filed
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.0.25 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
IntelliJ IDEA terminal
Additional Information
_No response_
100 Comments
---
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
---
not a duplicate -- all the false positives listed above have different error messages
Experiencing a similar issue with CC throwing 400's.
Claude Code Version: 2.0.25
Login method: Claude Max Account
Model: Sonnet 4.5 (default)
OS: GNU Guix
TERM: xterm-256color (via ssh)
how do you get the API response codes from the CLI ?
It just logged them. I did nothing special.
Can you please elaborate ? I am also facing a similar issue but not sure if it is the same in vs code terminal. I wanted to verify if it is the 400 response as you have mentioned. It would be nice if you can share how you log the claude-code's API calls.
Please & thank you
I lost the logs
I'm getting a similar error after attempting to
/resumea Claude Code terminal session:It's displayed the first time I send a message since
/resume. Disabling thinking (pressing tab) resolved it.@zubin how do you get the API calls logged ?
I didn't do anything log-related. Just tried
/resumeand that text was printed.I am getting this also. As a workaround, instead of a long prompt, I am writing my prompt to a .md file and then instructing claude-code to read and implement the instructions in the .md.
experiencing similar issue too, here is the log
This started happening to me a few hours ago when using
/resumein Claude Code cli. Functions without issue in other cases.similar issue.
API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"messages.9.content.1:
thinkingorredacted_thinkingblocks in the latest assistant message cannot be modified. These blocks must remain as they were in theoriginal response."},"request_id":"req_011CVQSt71zTgpRrWfMKDwaZ"}
claude code 2.0.50 , Max user, ubuntu(wsl)
rewinding 2 or 3 messages usually solves this issue for me.
similar issue. 2.0.50 (Claude Code), Pro User, Mac os
⎿ API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"messages.9.content.24:
thinkingorredacted_thinkingblocks in the latest assistant message cannot be modified. These blocks must remain as they were inthe original response."},"request_id":"req_011CVQp9vC1ZRNZGKGyJHSCm"}
same problem, turning thinking off does not fix the problem
I just got this for the first time.
+1, lost all progress, gotta copy to restart, any best recovery method?
compaction via /compact seemed to fix it for me
Same here, but compacting does not fix the issue either (nor does disable thinking, or going back to the previous message).
Same
Once it is happened, I can't use /compact and request any prompt more
Workaround that worked for me:
I encountered the same error and was able to resolve it by manually removing thinking blocks from the conversation JSONL file.
Steps:
This is a temporary workaround until an official fix is released. Hope this helps others facing the same issue!
@nahyeongjin1
Sounds good! Thanks for sharing!!
I'll try it next time it comes up.
@nahyeongjin1 this workaround worked perfectly for me, thank you 🙏🏽
I ran into a same error when using Claude Code Cloud when I tried to edit my last message a few seconds after it started processing the request. Was using Opus 4.5
Every subsequent message after that had the same error, rendering that thread effectively frozen, unable to iterate on any more changes
My edited prompt:
The response:
the simplest is to rewind ur last or 2,3 messages before.
it works always for me.
got the same issue, compact did not work and this workaround script from @nahyeongjin1 worked like a charm 👍
```python
with open(filepath, 'r') as f:
lines = f.readlines()
with open(filepath, 'w') as f:
for line in lines:
if not line.strip():
continue
data = json.loads(line)
if 'message' in data and 'content' in data['message']:
if isinstance(data['message']['content'], list):
data['message']['content'] = [
c for c in data['message']['content']
if c.get('type') not in ('thinking', 'redacted_thinking')
]
f.write(json.dumps(data) + '\n')
will add that this is still occurring VERY reliably with claude agent sdk , I can reproduce quite consistently on v0.1.70
I can confirm this solution works. Anthropics, what are we paying for? Please fix this bug. Solution has already been found for you. I'm tired of this bug plaguing me in VS Code extension.
this is terrible, happening very often.
Yes, curently happening on really many chats. maybe 50%
and in vscode you cannot press tab to exit thinking ;)
and by the way, no reaction here? Anthropic does not care that their product is degrading rapidly?
+1 same issue
+1 same
+1 Same. This is a very disgusting bug, and it occurs frequently. Please fix it as soon as possible.
+1 Same. Still happening with version 2.1.1. Please fix this!
+1
+1
API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"messages.1.content.13: thinking or redacted_thinking blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response."},"request_id":"req_011CWxP1n9XTs4dTcRPGCmz6"}
+1 This happened to me on claude code web app on mobile.
Hi! I encountered the same issue and want to share what worked for me.
After some troubleshooting, I found that starting a new conversation resolved the 400 error.
My friends suggested this might be related to the context becoming too long in extended conversations. While there's a "compress context" option available, they mentioned that starting fresh is probably the better solution, as compressed contexts might still cause issues down the line.
I'm not entirely sure if this is the root cause for everyone, but I noticed some people mentioned experiencing this around 50% of the time, which could support this theory.
Hope this helps! 😊
Hi everyone,
I’ve put together a quick-fix plugin that uses a
hook/commandto work around the issue.You can find it here: https://github.com/a1exlism/claude-code-cmd-fix-thinking-error
it happens for me now in over 50% ofg all chats. often also very close afters starting so there is no correlation with the used context. it destroy productive work a lot since I am now used to start every chat with "the other cha crashed again, please read my copy&paste..."
API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"messages.11.content.22: thinking or redacted_thinking blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response."},"request_id":"req_011CX5CRxigYG8J8ddVwUyLb"}
I’ve hit this twice in CLI (-p) mode on Linux, and both times the session became unrecoverable. It looks like state corruption: retrying the same prompt just repeats the error forever.
claude -p --output-format json)Impact: once it happens, immediate retries keep failing and you have to start a new session.
UPDATE:
According to the status report at https://status.claude.com/incidents/5dvcj03m4d57, Anthropic is currently addressing this issue. We should consider updating the CLI version once a fix is released.
I still experience the issue with vscode on Linux with CC 2.1.22 on January 28th
I was able to Ctrl+C out of the session and resume with
claude --resumeand things are working again. Mac OS Ghostty terminal, Claude 2.1.19.Ctrl+C and resume did not work for me. Same specs as above.
still happening through opencode as well
New trigger path: ESC during extended thinking → empty text content block
Version: 2.1.30
Platform: macOS (Darwin 23.3.0)
Model: claude-opus-4-5-20251101
Error
Reproduction
/compact, everything returns the same 400)What's happening (from debug logs)
The interrupt during extended thinking saves the partially-generated assistant message. Since the model was still in the thinking phase and hadn't produced any visible text, the saved message contains an empty text content block. This poisons the conversation history — every subsequent API call sends the same broken history.
Debug log sequence:
Note: between the interrupt (02:15:05) and the first error (02:17:19), one normal turn completes successfully. The empty text block from the interrupted response is in the history but only causes failures when the conversation gets long enough or the message ordering hits the validation check.
Relationship to other variants
This is the same conversation-state-corruption class as the thinking block modification errors in this issue, but via a different code path:
"thinking or redacted_thinking blocks in the latest assistant message cannot be modified""text content blocks must contain non-whitespace text"Also related to #22923 (exact same error, different trigger) and #11323 (same trigger, different error variant).
Suggested fix
Claude Code should validate/sanitize assistant messages after an interrupt — if a text content block is empty (or contains only whitespace), either remove it or replace it with a placeholder like
"[interrupted]"before saving to conversation history.I got the same issue when i pressed the ESC key when it was thinking
Error:
Not a perfect solution, but here's a workaround that worked for me to get past this error:
/resumein the chatAPI:400error/resumeto continue your conversation from there+1
The way you do this in Claude Code CLI is to hit Esc twice, then press the Up arrow one or more times, then hit enter twice to rewind the conversation and continue from there. This drops the corrupted part of the chat history.
same issue here as well, the only way I could get past it was abandoning the session
Same exact issue as well. Happened while I halted an extended session with esc and tried to tell Claude something else.
In my case, I could revive the session through ctrl + c -> claude resume.
same issue:
Sorry, your request failed. Please try again.
Copilot Request id:
GH Request Id:
Reason: Request Failed: 400 {"message":"messages: text content blocks must contain non-whitespace text"}
I switched from one agent to another (after I changed from Autopilto to Default Approvals)
Same here after
/resume/tmp/gh-comment-10199.md
Hit this today on Claude Code v2.1.112 (macOS 15.3.2).
request_id:req_011Ca9dV75e7W49c6Y5nyuz1messages.17.content.1: \thinking\or \redacted_thinking\blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.Session became unrecoverable — every subsequent prompt hit the same 400.
/clearwas the only way out.Hit this today and got it working. Root cause: extended-thinking responses come back signed, and on resume the API validates those signatures. If anything has touched them (hook, compaction, half-written entry from a crash) they no longer match and every retry fails — each retry just appends another broken turn.
Fix: strip
thinking/redacted_thinkingblocks from the session's jsonl. The API doesn't validate what isn't there; you only lose the model's private reasoning — text, tool calls, and tool results stay intact.Easiest way if you don't want to touch the transcript by hand — open a fresh Claude Code session and paste this:
Then
claude --resume <session-id>and it picks up clean.Two things that don't work, skip them: truncating the jsonl to before the first failure (Claude Code reloads from the tail, so you lose recent context instead of rewinding), and editing thinking block content in place (signature still won't match — it has to be fully removed).
Hi, is there any update on how to resolve this issue without disabling thinking?
Still hitting this in 2026 — the thinking block corruption is brutal because it makes the entire session unrecoverable. Cozempic (https://github.com/Ruya-AI/cozempic) has a
thinking-blocksstrategy that strips stale thinking content from session history, which prevents the immutable-block replay that triggers the 400.pipx install cozempic && cozempic treat --rx standardshould clear the corrupted state without losing your conversation. Happy to hear if it resolves the loop for anyone still affected.This thinking block corruption is one of the most frustrating session-killers. Cozempic's
thinking-blocksstrategy strips extended thinking content from older turns in the JSONL before it can cause these 400 errors — the blocks stay structurally valid but the bulk content that triggers modification errors gets cleaned. Thecozempic doctorcommand also detects corrupted session state.pip install cozempic && cozempic doctorto diagnose, thencozempic treatto fix. Repo: https://github.com/Ruya-AI/cozempic — feedback welcome.Try to start a new conversation.
This could fix this error
Starting a new conversation does in fact fix the issue but you lose the context from previous conversation with out any summary or context dump
Yeah, but I don't find any way to resolve this issue other than this.
same there
Passed session id in a new chat and asked it fix the error
<img width="1184" height="348" alt="Image" src="https://github.com/user-attachments/assets/78c9dfbe-6ce2-4b6c-b4c1-ea06578055e4" />
Same error just now
Ran into the issue now
⎿ API Error: 400 messages.9.content.19:
thinkingorredacted_thinkingblocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.The session was replaying thinking blocks that had empty text but live signatures, so the API rejected them. Stripping those broken blocks from the active branch and relinking the chain preserved all messages, tool calls/results, and the real thinking blocks elsewhere.
yep happening to me too, latest version of the app on Mac, across three separate sessions - not easily fixable in the app (as oppose to the CLI)
API Error: 400 messages.1.content.32:
thinkingorredacted_thinkingblocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.<img width="302" height="429" alt="Image" src="https://github.com/user-attachments/assets/3a936166-e08c-485c-90f2-c68fb5debb98" />
Got hit by this twice today in Claude Code and you can't recover from it as it seems
Same here
<img width="1277" height="92" alt="Image" src="https://github.com/user-attachments/assets/b817cef9-ee46-47f7-8cbf-23f284d44fb6" />
● Bash(echo "PROBE_$(date +%s)_END")
⎿ Cancelled: parallel tool call Bash(D=/data/pulkitag/scratch/rli14/projects/…) errored
Read 1 file (ctrl+o to expand)
⎿ Cancelled: parallel tool call Bash(D=/data/pulkitag/scratch/rli14/projects/…) errored
⎿ API Error: 400 messages.9.content.30:
thinkingorredacted_thinkingblocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.Opus 4.8 broken
yep if you use Sonnet all is good, how can I switch to 4.7?
same here with opus 4.7 but seems they released opus 4.8, switching to 4.8 works
nope, I had the same problem with 4.8
same here with opus 4.8
API Error: 400 messages.1.content.14:
thinkingorredacted_thinkingblocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.Same looks like a wide spread problem
i'm on opus 4.7 and it's happening there too
Posted by Claude Code acting on behalf of this account's owner, per their instruction to add JSONL-mined diagnostic evidence to this thread.
TL;DR: Forensics from two JSONL session files on CC 2.1.154 +
claude-opus-4-8(claude-opus-4-7unaffected on same CC version) confirms @zoratu's "empty text + live signatures" mechanism with hard data, and identifies the structural trigger: Opus 4.8 appendsthinkingblocks after paralleltool_usebatches in the same assistant message — placing them at deep content indices (e.g.content[12]) where message reconstruction fails on cancel.<details>
<summary>Full JSONL evidence</summary>
Environment
2.1.154— bug persists past the2.1.153reports earlier in this threadclaude-opus-4-8— same sessions withclaude-opus-4-7on2.1.154are unaffected---
Finding 1 — Thinking blocks stored as empty content + live signature
Every
thinkingblock in both affected sessions appears in the raw JSONL as:CC strips the CoT text for JSONL storage but preserves the Anthropic-issued signature. On reconstruction after a parallel-cancel event, this empty-content block is sent to the API. The API validates content against signature, finds a mismatch → 400. This is the concrete JSONL evidence behind @zoratu's observation ("replaying thinking blocks that had empty text but live signatures").
---
Finding 2 — Opus 4.8 uses interleaved thinking (thinking blocks AFTER tool_use batches)
In the
parentUuidchain of an affected assistant turn, Opus 4.8 appends athinkingblock after a parallel batch oftool_usecalls within the same assistant message:All 7
tool_resultentries arrive simultaneously (parallel dispatch).claude-opus-4-7only placesthinkingat the start of a response (before anytool_usecalls).claude-opus-4-8's post-batch interleaved thinking places the block at a deep content index (e.g.content[12]) — a shape that message reconstruction apparently doesn't handle correctly when one of the parallel tools cancels.This also explains why the partial fix in
2.1.152(strip stale signatures on model/login switch) didn't cover this path: the interleaved position of the block inside a long tool-call sequence is the new factor that the strip logic doesn't reach.---
Finding 3 — Same content index on every retry = corrupted payload stuck in session
The error
messages.1.content.12appeared identically across all three retries in one session over a 14-minute window. CC is replaying the exact same corrupted payload on every turn — the poisoned history is locked in session state./clearis the only escape; Escape/retry does not help.</details>"
Here's a python script to repair the broken blocks. It should work on the CLI and Desktop apps. SessionStart hooks can be added so that transcripts self-heal. https://github.com/zoratu/fix-claude-thinking
Extended thinking: the agent loop freezes on a thinking-block signature boundary — a recovery gap, not an API fault
Status: independent investigation write-up + minimal repros + recommended client-side fix.
Models: reproduced identically on
claude-opus-4-8,claude-opus-4-7,claude-sonnet-4-6,claude-haiku-4-5(the full current family).SDK:
anthropic(Python) 0.96.0.---
0. Framing (read first)
This is not "the API throws a wrong error." The 400 is a legitimate integrity boundary working as designed: thinking blocks are signed, and the API rejects one whose signature isn't authentic. A client hits that boundary through avoidable clumsiness (capturing an unsigned partial block, re-serializing the signature, echoing output-only fields). The only genuine defect is what happens next — the harness re-sends the identical rejected request forever and the session freezes. Backtrack, not die. Everything below supports that framing.
1. Summary
With extended thinking enabled and any continuation that re-sends a prior assistant turn (tool use, or multi-turn), the Messages API returns a deterministic 400 if a
thinking/redacted_thinkingblock anywhere in the request carries a signature that doesn't verify. The error is correct and self-locating (messages.N.content.M).The client defect: on this 400 the harness re-sends the identical conversation state and retries forever — every subsequent user input reproduces the same 400, and the session is permanently wedged. The bug is the missing recovery path, not the 400.
2. Symptom
Two surface messages, one root cause (the thinking-block signature fails to verify):
Then the harness retries; every following turn returns the same 400; the session cannot be steered out of the loop.
3. Root cause — the true invariant (empirically characterized)
The API checks that **every thinking/
redacted_thinkingblock present in the request carries an authentic, uncorrupted signature.** Precisely characterized by experiment:**(a) It validates all assistant turns, not just the latest. Corrupting the signature of a thinking block in a non-latest** assistant turn 400s at that turn's coordinate — even though the error string says "latest assistant message." The wording is narrower than the actual validation.
**(b) The signature is an authenticity token, not bound to content/position/block/model.** A boundary table — one captured signed thinking turn, each mutation applied and resent (identical across the full current family —
claude-opus-4-8/4-7,claude-sonnet-4-6,claude-haiku-4-5— and identical for latest vs non-latest turns):| mutation of a thinking block | verdict |
|---|---|
| (unchanged baseline) | ACCEPT |
| edit the thinking text (keep signature) | ACCEPT |
| truncate / empty the text (keep signature) | ACCEPT |
| drop the thinking block entirely | ACCEPT |
| reorder blocks | ACCEPT |
| replace the signature with a different but authentic signature (even from another model) | ACCEPT |
| corrupt the signature (or send it missing) | REJECT 400 at the block's coordinate |
So "cannot be modified" operationally means: if a thinking block is present, its signature must still verify; you may otherwise edit, truncate, reorder, or omit it — and any genuine signature passes. The check is authenticity, not binding to the block's text, position, or issuing model.
4. Why it freezes
The 400 is deterministic and names
messages.N.content.M. A robust client has at least three safe recoveries, none requiring any API change:Instead the client keeps the invalid block and re-sends the identical request → identical 400 → infinite loop. New user messages don't help — they're appended after the poisoned block, which still fails validation.
5. How the bad state arises (two confirmed triggers)
5.1 In-place mutation on resend
The harness reconstructs/serializes the assistant turn and the thinking block's signature is altered or a rebuilt block is substituted. →
cannot be modified.5.2 Mid-stream interrupt (confirmed)
When streaming a thinking turn, the signature arrives last (a
signature_delta, immediately before the thinking block'scontent_block_stop). Interrupting before it — e.g. the user sends a message while the turn is still streaming — captures a thinking block with text but no signature. Resending it →Invalid signature. (Reproduced: a streamed turn interrupted mid-thinking_deltayieldssignature=''; resending rejects atmessages.1.content.0, while the same turn allowed to finish is accepted.)6. Minimal reproductions (self-contained)
(The repros below use
claude-sonnet-4-6withbudget_tokens; onclaude-opus-4-7/4-8swap tothinking={"type": "adaptive"}— the boundary is identical.)6.1 Signature variant
6.2 Mid-stream interrupt variant
(Additional confirmations not shown for brevity: corrupting a NON-latest turn's signature rejects at that turn's coordinate; a thinking block accepts a different-but-authentic signature, including one issued by a different model; a corrupted signature is rejected even cross-model.)
7. Recommended fix (client-side; no API change required)
messages.N.content.M, drop/restore that block, resend once; if it recurs, rewind rather than loop.content_block_stop(the signature arrives last). A partially-streamed thinking block is unsigned and must never be re-sent.parsed_outputround-tripped viamodel_dump()triggers a separate 400 (messages.N.content.M.text.parsed_output: Extra inputs are not permitted).8. Two honest nuances (not client clumsiness)
9.
redacted_thinking(note)redacted_thinkingblocks (returned when the reasoning is flagged by a safety classifier mid-generation) carry their content + integrity in an opaquedatafield rather than asignature. The same contract should apply (they are signed, integrity-protected blocks that must be passed back), so a client guard must treatdataas the protected field for redacted blocks. This was not empirically verified here — redacted blocks can't be elicited on demand (they require the safety classifier to fire on reasoning) — so it is stated as expected-by-contract, not measured.10. Related issues + coverage (honest scope)
Triggered by #10199; bears on a cluster of reports. Scope, stated so this doesn't over-claim:
Unifying takeaway: most of this cluster is the same "deterministic resend-400 + no recovery = freeze" pattern. §7.2 fixes the pattern; §3/§5 characterize the dominant (signature) trigger.
11. Expected vs actual
This worked great, thanks!
@ThariqS @bcherny in case you haven't noticed yet, Claude Code is b0rked with Opus v4.8 (see above)
Still reproducing this on a much newer Code version, and with a notably different
trigger pattern than OP's. Adding evidence in case it helps narrow it.
Environment
2.1.154(cc_version=2.1.154.230,cc_entrypoint=cli)v22.14.0claude-opus-4-8[1m]What's different from OP
OP reproduced on a long session (~14,976 lines,
messages.71.content.8) andhypothesized repeated questions / message-edit retries as contributing factors.
I'm reproducing on short, clean sessions — error indices
messages.1.content.24,messages.1.content.8, andmessages.3.content.8across three separate failures.So conversation depth and repeated questions aren't required; the bug fires on a
first-or-second assistant turn that contains interleaved thinking plus a multi-tool
Bash burst.
Reliable reproduction (~30 seconds, fresh session)
claude --debugfrom a directory with a few git repos as immediate children.~/path/to/src, then after Iapprove, run a few
git/lsbash calls to do it."_issuing additional probe Bash calls (
pwd,echo hello,/bin/echo,true,isolated
git rev-parse,env -i /bin/bash,printf 'PROBE_OK') — appears tobe self-diagnostics after detecting something wrong in tool output.
/v1/messagesrequest 400s.Controls I ran first that all pass on the same install:
short single-tool plan-mode flows work in practice)
The failure requires plan-mode-exit transition + multi-tool turn + interleaved thinking.
Workaround for users hitting this: run multi-step commands in normal mode (Tab to
toggle out of plan mode).
Server-side identifiers for this failure
request_id:req_011CbVTRgxcdKgkdc5GRMurDx-client-request-id:f3f45405-61b8-4fc1-9e82-0a5698cbfd502026-05-28T18:40:45.196Z(request) →…45.441Z(400)attempt 1/11 … 11/11) 400 identically with the samerequest_id.What
--debugshows right before the failureTwo code paths look implicated.
(1) Dynamic tool loading re-walks message history before each main-loop dispatch.
Immediately before the 400:
If that walker is re-serializing assistant content blocks in place, it would
invalidate signed thinking blocks.
(2) Auto-mode classifier firing six times concurrently against the same conversation.
Between 18:40:00 and 18:40:28 (i.e. during the live turn):
transcriptEntriesandmessagesstay constant at4and14, buttools=fluctuates across consecutive classifier preparations:
3455, 3157, 3200, 2921,. The tool inventory being serialized is mutating mid-turn while the2987, 2978
main loop is also active — that's consistent with shared mutable state between
the main loop and the auto-permission classifier path.
Stack trace
Ruled out from my side
sessions (
/clearandclaudefrom a fresh shell), not resumed.~/.claudestate — full integrity check after a hard powercycle the same morning: all top-level JSON parses, all per-project
sessions-index.jsonparse,history.jsonllast line parses, all newestsession transcripts parse.
the thinking-block layer per the hooks reference ("cannot block the message or
change what is stored in the transcript or sent to Claude").
2.1.154is current at the time of repro.Happy to share the full ~970-line
--debuglog and the prior session transcriptsif useful.
I'm seeing this on Opus 4.8 version 2.1.145
I spotted complaints from the agent about tool calls returning no results, and I also saw this before one instance of this happening:
and then a little after...
edit: in another session, just prior to getting the error:
Another observation - I can go for a while with everything going fine, and then I'll see _all_ of my agents start getting blank or corrupted tool call results, and shortly after they'll hit the issue.
I've had some luck using the following script to un-block sessions, but it doesn't stop the problem. Just helps avoid losing work.
https://gist.github.com/robertfw/993dbe8643c4fbdf12005dff2eca1f90
Thanks @robertfw - I've just tried that and it works well! Did they also just recently reset quotas?
Another reproducible trigger on 2.1.154: a heredoc inside a _parallel_ Bash batch → orphaned
tool_useblocks → permanent 400Adding a concrete, deterministic trigger that fits the "blank/cancelled tool results shortly before the 400" pattern several others have reported in this thread. We hit it across multiple autonomous agents; sharing the mechanism, a redacted trace, and the agent-side mitigation that stopped it for us.
Environment
2.1.154, macOS (Darwin, arm64)/loopauto-resumeHow it happens (trigger)
A single turn emitted several Bash calls in parallel. One was a multi-line command containing a heredoc (
<<'EOF' … EOF). The heredoc terminator got split off and ran as its own command (EOF→command not found, exit 127). That error cancelled the sibling parallel calls, leaving theirtool_useblocks with no matchingtool_result— in an assistant turn that also contained signedthinkingblocks.(The same batch also had an unquoted URL containing
?ref=<sha>→ a zshno matches foundglob failure. A second way these batches die, same root cause: hand-built shell strings with metacharacters.)Why it wedges
On the next request, the prior assistant turn's
thinkingblocks must be re-sent verbatim. With the batch partially cancelled and the turn in a modified state, the resend is rejected:The API rule itself is intentional — the problem is that the harness gets into an unsendable state and cannot get out: the corrupted turn stays in history, so every subsequent request — including each
/loopwakeup — replays it and 400s identically. Unrecoverable.Redacted trace
(identifiers genericised:
<repo>,<path>,<sha>,<dir>)How to recover
No in-session recovery — the poisoned turn is fixed in history. We abandon the session and start a fresh one. (Others in this thread have posted transcript-surgery scripts that strip/repair the offending block as a partial workaround.)
How we mitigated (agent-side, until a fix lands)
Because the trigger is deterministic, we codified three rules for our agents:
cat <<EOF,echo >)./loop/ auto-resume must start a fresh session on an unrecoverable 400 rather than replay the transcript.(1) and (2) remove the trigger; (3) stops a transient failure from becoming permanent. Happy to add more detail if useful.
Same error reproduces on a fresh single-turn session under remote control (not deep history)
Hit
API Error 400: messages.1.content.14: thinking or redacted_thinking blocks in the latest assistant message cannot be modified— but the conditions are different from the original report, and I think they localize the bug:/clear, with a single user request. Not a long/deep session.alwaysThinkingEnabled: true)./clearand re-running reproduce it identically; a fresh request hits the same 400 at message 1.Working hypothesis: the assistant turn gets re-serialized across the remote-control relay between phone and Mac, re-encoding a thinking block so it no longer matches its signature → API rejects it. That would explain why it fails on turn 1 and why the local v2.1.156 fix doesn't cover it (different code path).
Workaround that avoids it: setting
alwaysThinkingEnabled: false(no thinking blocks for the relay to touch).Environment: macOS, Claude Code CLI v2.1.156, remote control via mobile app.
No bug triggered when resort to Opus 4.7:
claude --dangerously-skip-permissions --model claude-opus-4-7is it on 4.8 only ? any fix guys
Switching to 4.7 helps
I’m not sure this is consistently tied to heredoc usage. I’ve also noticed the issue with 4.7 in sessions that don’t use heredocs.I put together a quick script that worked for me to identify affected sessions: https://gist.github.com/chid/aa4169a086ccea78545611c1e0963251After identifying them, I used the fix above to strip the affected session data https://github.com/anthropics/claude-code/issues/10199#issuecomment-4569303860. It then successfully resumed.
No, I've seen this on 4.7 as well.
Today I just saw this:
I run multiple agents and keep an eye on all (well most) of them one one screen, and I'll see this start to occur across all of them at the same time.
I think this has to do with the increased measures of hiding thinking text from the agent (to prevent distillation/training by other LLMs), and it's either being overzealous or being applied wrong, or the harness/agent doesn't know how to deal with the hidden responses.
still on opus 4.8
burning my tokens and giving me error in the end "opus 4.8 Thinking Effort = Max" any timeline when this will be fixed?
note: thinking effort = High is working. it is only "Max" throwing an error for me