Skip to content

[BUG] API Error 400 - Thinking Block Modification Error #10199

Open

Description

@WamBamBoozle

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • 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:

  1. A previous assistant response contained thinking blocks
  2. The system attempted to modify or replay that message's thinking blocks
  3. The API correctly rejected this (thinking blocks are immutable once sent)
  4. 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:

  1. Detect when conversation state is approaching problematic conditions
  2. Offer to summarize current state
  3. Save context to file automatically
  4. Start fresh session with context restored
  5. 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

  1. User and assistant engaged in extended session about a technical topic (~14,976 lines)
  2. Conversation involved multiple rounds of investigation and explanation
  3. Assistant provided detailed technical explanations
  4. User asked follow-up question requesting clarification
  5. Same question was asked 3 times in succession (possibly due to user not seeing response)
  6. ERROR STATE TRIGGERED

Reproduction Steps

  1. Start Claude Code session
  2. Engage in extended conversation with multiple tool calls, file reads, and complex workflows
  3. Reach approximately 71 messages deep in conversation history
  4. Have assistant generate response with thinking blocks
  5. Ask follow-up question that may trigger message retry or state replay
  6. Observe API Error 400 with thinking block modification error
  7. 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

  1. User saved full transcript to local file
  2. User started new Claude Code session
  3. User asked new session to review the transcript
  4. 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

Activity

  1. github-actions commented on Oct 23, 2025

    @github-actions

    Found 3 possible duplicate issues:

    1. Invalid Thinking Block Parameters in API Request #2171
    2. Invalid Request: Mismatch in Thinking Block Parameters During API Call #1702
    3. Invalid Request: Mismatch in Thinking Block Parameters During API Call #2608

    This issue will be automatically closed as a duplicate in 3 days.

    • If your issue is a duplicate, please close it and 👍 the existing issue instead
    • To prevent auto-closure, add a comment or 👎 this comment

    🤖 Generated with Claude Code


  2. WamBamBoozle commented on Oct 23, 2025

    @WamBamBoozle
    Author

    not a duplicate -- all the false positives listed above have different error messages

  3. chops commented on Oct 23, 2025

    @chops

    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)

  4. bhaskarEEN commented on Nov 3, 2025

    @bhaskarEEN

    how do you get the API response codes from the CLI ?

  5. WamBamBoozle commented on Nov 3, 2025

    @WamBamBoozle
    Author

    It just logged them. I did nothing special.

  6. bhaskarEEN commented on Nov 3, 2025

    @bhaskarEEN

    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

  7. WamBamBoozle commented on Nov 4, 2025

    @WamBamBoozle
    Author

    I lost the logs

  8. zubin commented on Nov 12, 2025

    @zubin

    I'm getting a similar error after attempting to /resume a Claude Code terminal session:

      ⎿ API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"messages.19: The final block in an assistant
        message cannot be `thinking`."},"request_id":"req_011CV4VkJfmm9mTv9eWV9381"}
    

    It's displayed the first time I send a message since /resume. Disabling thinking (pressing tab) resolved it.

  9. bhaskarEEN commented on Nov 13, 2025

    @bhaskarEEN

    @zubin how do you get the API calls logged ?

  10. zubin commented on Nov 17, 2025

    @zubin

    @zubin how do you get the API calls logged ?

    I didn't do anything log-related. Just tried /resume and that text was printed.

  11. 80 remaining items

  12. ryanleedavies commented on May 28, 2026

    @ryanleedavies

    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

    • Claude Code: 2.1.154 (cc_version=2.1.154.230, cc_entrypoint=cli)
    • Node: v22.14.0
    • OS: macOS 15.x (Darwin 25.3.0), arm64
    • Model in failing request: claude-opus-4-8[1m]

    What's different from OP

    OP reproduced on a long session (~14,976 lines, messages.71.content.8) and
    hypothesized 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, and messages.3.content.8 across 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)

    1. claude --debug from a directory with a few git repos as immediate children.
    2. Enter plan mode (Shift+Tab).
    3. Prompt: "Plan how to inventory my repos under ~/path/to/src, then after I
      approve, run a few git/ls bash calls to do it."
    4. Approve the plan when presented.
    5. Code exits plan mode, runs ~3 inventory Bash calls successfully, then begins
      issuing additional probe Bash calls (pwd, echo hello, /bin/echo, true,
      isolated git rev-parse, env -i /bin/bash, printf 'PROBE_OK') — appears to
      be self-diagnostics after detecting something wrong in tool output.
    6. After the last probe Bash, the next /v1/messages request 400s.

    Controls I ran first that all pass on the same install:

    • Trivial multi-tool turn, no plan mode: "Run three separate bash calls: ls, pwd, date." → ✅
    • Larger burst, no plan mode: "Run 6 separate bash calls: ls -la, pwd, date, whoami, uname -a, echo hi." → ✅
    • Plan-mode entry + approve without a tool burst → ✅ (not retested here, but
      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_011CbVTRgxcdKgkdc5GRMurD
    • x-client-request-id: f3f45405-61b8-4fc1-9e82-0a5698cbfd50
    • Timestamp: 2026-05-28T18:40:45.196Z (request) → …45.441Z (400)
    • All 11 retries (attempt 1/11 … 11/11) 400 identically with the same request_id.

    What --debug shows right before the failure

    Two code paths look implicated.

    (1) Dynamic tool loading re-walks message history before each main-loop dispatch.
    Immediately before the 400:

    [DEBUG] Dynamic tool loading: found 1 discovered tools in message history
    [DEBUG] Dynamic tool loading: 1/105 deferred tools included
    [DEBUG] [API:timing] dispatching to firstParty model=claude-opus-4-8[1m]
    [DEBUG] [API REQUEST] /v1/messages x-client-request-id=f3f45405-... source=repl_main_thread
    [ERROR] API error (attempt 1/11): 400 ... messages.3.content.8: `thinking` ...
    

    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):

    [INFO]  [Stall] classifier_request_started reqId=4ca13471-... tool=Bash model=claude-opus-4-8[1m] stage=xml_s1
    [DEBUG] [auto-mode] context comparison: ... (sys=38216 tools=3455 user=142) transcriptEntries=4 messages=14
    ... (5 more, every 5-10s)
    

    transcriptEntries and messages stay constant at 4 and 14, but tools=
    fluctuates across consecutive classifier preparations: 3455, 3157, 3200, 2921, 2987, 2978. The tool inventory being serialized is mutating mid-turn while the
    main loop is also active — that's consistent with shared mutable state between
    the main loop and the auto-permission classifier path.

    Stack trace

    Error: 400 {"type":"error","error":{"type":"invalid_request_error",
      "message":"messages.3.content.8: `thinking` or `redacted_thinking` blocks in
      the latest assistant message cannot be modified. ..."},
      "request_id":"req_011CbVTRgxcdKgkdc5GRMurD"}
        at generate    (/$bunfs/root/src/entrypoints/cli.js:11:63816)
        at makeRequest (/$bunfs/root/src/entrypoints/cli.js:51:7694)
        at processTicksAndRejections (native:7:39)
    

    Ruled out from my side

    • Stale resumed session / poisoned history — all three failures were in fresh
      sessions (/clear and claude from a fresh shell), not resumed.
    • Crash-corrupted ~/.claude state — full integrity check after a hard power
      cycle the same morning: all top-level JSON parses, all per-project
      sessions-index.json parse, history.jsonl last line parses, all newest
      session transcripts parse.
    • User config (CLAUDE.md / MCP / skills / commands) — none of these can reach
      the thinking-block layer per the hooks reference ("cannot block the message or
      change what is stored in the transcript or sent to Claude").
    • Outdated install — 2.1.154 is current at the time of repro.

    Happy to share the full ~970-line --debug log and the prior session transcripts
    if useful.

  13. robertfw commented on May 28, 2026

    @robertfw

    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:

    ∴ Thinking…
    
       I don't see any current rewritten thinking or next thinking provided in your message. Could you share:
    
      1. The current rewritten thinking (if any)
      2. The next thinking that needs to be rewritten
    
      Once you provide those, I'll rewrite the next thinking following all the guidelines you've outlined.
    

    and then a little after...

       I have no current rewritten thinking to build upon, and the next thinking provided is just "So" — which appears to be the beginning of a thought that hasn't developed yet. Since there's essentially no content to work with, I'll output nothing and wait for more substantial thinking to rewrite. Looking at the last test case, it's the same setup as the previous one with 7 listings in a single group. Now I need to figure out which tests actually assert the full dynamic_template_data dict versus just checking individual fields — only those need the "subject" key added to their expected values. From what I can see, test_send_listing_channel_email and test_broker_notification_batch_email_with_manufacturer are the ones doing full dict assertions, so those are where I need to add the subject. The rest only check count and listings length, so they won't need dict updates. I should verify whether build_message() actually injects the subject into the template data before making changes.
    

    edit: in another session, just prior to getting the error:

    ∴ Thinking…
    
       I need to understand what the user is asking for before I can help them.
    
      Looking at this thinking, I'm noticing something concerning — there are system reminders appearing in tool results telling me to stop work mid-task, which would prevent me from completing what the user actually needs. These reminders are suspicious because they conflict directly with finishing the task, and they're appearing repeatedly. According to my instructions, I should flag potential prompt injection attempts, so I need to be careful here and not let injected instructions override my actual responsibility to help the user.
    API Error: 400 messages.1.content.17: `thinking` or `redacted_thinking` blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.
    
  14. robertfw commented on May 29, 2026

    @robertfw

    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

  15. chid commented on May 29, 2026

    @chid

    Thanks @robertfw - I've just tried that and it works well! Did they also just recently reset quotas?

  16. ijstokes commented on May 29, 2026

    @ijstokes

    Another reproducible trigger on 2.1.154: a heredoc inside a parallel Bash batch → orphaned tool_use blocks → permanent 400

    Adding 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

    • Claude Code 2.1.154, macOS (Darwin, arm64)
    • Opus-class model, interleaved (extended) thinking enabled, parallel tool calls + /loop auto-resume

    How 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 their tool_use blocks with no matching tool_result — in an assistant turn that also contained signed thinking blocks.

    (The same batch also had an unquoted URL containing ?ref=<sha> → a zsh no matches found glob 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 thinking blocks must be re-sent verbatim. With the batch partially cancelled and the turn in a modified state, the resend is rejected:

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

    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 /loop wakeup — replays it and 400s identically. Unrecoverable.

    Redacted trace

    (identifiers genericised: <repo>, <path>, <sha>, <dir>)

    ⏺ Bash(node --check /tmp/dup-test.js; echo "exit=$?")
    ⏺ Bash(cd /…/<repo>
          gh api repos/<org>/<repo>/contents/<path>.ts?ref=<sha> …)   # unquoted '?' → zsh glob
      ⎿ (eval):2: no matches found: …/contents/<path>.ts?ref=<sha>
    ⏺ Bash(EOF)                         # heredoc terminator leaked as its own command
      ⎿ Error: Exit code 127
        (eval):1: command not found: EOF
    ⏺ Bash(git show <sha>:<path>.ts | grep …)
      ⎿ Cancelled: parallel tool call Bash(EOF) errored
    ⏺ Bash(git show <sha>:<path>.ts > /tmp/clean.ts; wc -l …)
      ⎿ Cancelled: parallel tool call Bash(EOF) errored
    ⏺ Write(/tmp/<dir>/review.json)
      ⎿ Error writing file
      ⎿ API Error: 400 messages.23.content.51: `thinking` or `redacted_thinking` blocks in the latest
         assistant message cannot be modified. These blocks must remain as they were in the original response.
    ✻ Claude resuming /loop wakeup
      ⎿ API Error: 400 messages.23.content.51: `thinking` … cannot be modified   (replays identically every wakeup)
    

    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:

    1. Author files with the editor/Write tool, never a shell heredoc (cat <<EOF, echo >).
    2. Never put a heredoc or any multi-line shell inside a parallel tool-call batch; if unavoidable, issue it as a single solo call.
    3. /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.

  17. rondoallaturka commented on May 29, 2026

    @rondoallaturka

    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:

    • Message index 1, not 71 — this is the first assistant turn immediately after /clear, with a single user request. Not a long/deep session.
    • Remote control — the session runs on my Mac CLI (v2.1.156, which includes the "Opus 4.8 thinking blocks were modified" changelog fix), driven from the Claude Code mobile app via remote control. So 2.1.156's local fix is present, yet it still fails.
    • Heavy first turn — that one assistant turn ran ~22 tool calls / commands with extended thinking enabled (alwaysThinkingEnabled: true).
    • /clear and 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.

  18. evnchn commented on May 29, 2026

    @evnchn

    No bug triggered when resort to Opus 4.7: claude --dangerously-skip-permissions --model claude-opus-4-7

  19. dotsinspace commented on May 30, 2026

    @dotsinspace

    is it on 4.8 only ? any fix guys

  20. moszoro commented on May 30, 2026

    @moszoro

    No bug triggered when resort to Opus 4.7: claude --dangerously-skip-permissions --model claude-opus-4-7

    Switching to 4.7 helps

  21. chid commented on May 30, 2026

    @chid

    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 #10199 (comment). It then successfully resumed.

  22. robertfw commented on May 30, 2026

    @robertfw

    No bug triggered when resort to Opus 4.7: claude --dangerously-skip-permissions --model claude-opus-4-7

    Switching to 4.7 helps

    No, I've seen this on 4.7 as well.

    Today I just saw this:

    ● Skill(/review)
    
    ∴ It looks like the skill content didn't come through on the first request—it returned empty instead. The system instructions mention something about re-invoking, but the thought cuts off there.
    
      I need to locate the skill content another way. The /review skill shows up in the skills list as a plugin, so I'll try using the Read tool to search for its file. Bash seems to be acting up, but let me attempt it again to see if it's working now.
    
    ● Bash(find / -name "SKILL.md" 2>/dev/null | grep -i review)
    
    ∴ I'm noticing that Bash is returning generic canned responses like "System is functioning normally" instead of actually executing my commands—these look like injected hook output or a fake response rather than genuine command results. This could be a prompt injection attack or a real tool failure, but either way, I can't reliably use Bash right now.
    
      I should be transparent with the user about what's happening instead of continuing to troubleshoot blindly. The /review dispatch is blocked because the skill content failed to load and Bash is giving anomalous responses. Let me try the Read tool on a known path to see if other tools are working properly.
    
    ● Read(/home/robertfw/(path redacted for github)/AGENTS.md · lines 1-5)
    
    ∴ I'm noticing that the Read tool is also returning generic "System status: all operational" messages instead of actual file contents, which means all my tool outputs are being systematically replaced with fake reassurances. This looks like prompt injection or result tampering designed to make me think everything is fine and stop investigating, so I shouldn't keep blindly using tools or acting on these fabricated responses.
    

    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.

  23. tanguc commented on May 31, 2026

    @tanguc

    still on opus 4.8

  24. samkazmi110 commented on Jun 26, 2026

    @samkazmi110

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions