Your Claude Code Session Is Permanently Stuck on a 400. Don't /clear — Do This.
You hit Esc, you typed "continue," and Claude Code answered with this:
API Error: 400 … 'thinking'/'redacted_thinking' blocks in the latest assistant
message cannot be modified. These blocks must remain as they were in the
original response.
So you try again. Same error. You wait and retry. Same error. The session is now a brick — every single message reproduces the exact same 400, and nothing you type from inside the session can get past it. The official advice is /rewind (which re-runs whatever happened since the checkpoint) or /clear (which throws the conversation away). Both work. Both are also the wrong tool if the thing in that context window is three hours of hard-won state you don't want to rebuild.
There's a third option that keeps everything. It's a two-line surgical edit of one file. But first you have to understand why the session is stuck, because the reason tells you exactly what to cut.
A real, named failure mode
This isn't a fluke. It's one of the most-reported classes of bug on the anthropics/claude-code tracker:
- #63147: "Resuming an extended-thinking session fails permanently with 400 'thinking blocks cannot be modified'" — and the title names the mechanism: the transcript stores the thinking text as empty but keeps the signature.
- #64003: the same 400, specifically after upgrading to Opus 4.8.
- #12225, #10199, #12130: the same immutable-thinking-block 400 across earlier versions and multi-turn conversations.
Different versions, different triggers, one shape. A fix landed for one variant in v2.1.156, but the interrupted-stream variant still reproduces, which is why this keeps coming back.
The contract that's being violated
Extended thinking has a rule that's easy to miss: once the API sends you a thinking (or redacted_thinking) block, that block is immutable. It comes back with a cryptographic signature, and on every subsequent turn you must send it back byte-for-byte as you received it. The signature is how the server verifies you didn't tamper with the model's reasoning. Change so much as one character — or send a block whose text no longer matches its signature — and the server rejects the whole request with a 400. That's by design; it's an integrity check, not a bug.
Now layer on how Claude Code stores a session. The transcript is a .jsonl file — one JSON object per line — at ~/.claude/projects/<slug>/<session-id>.jsonl, where <slug> is your working directory with the slashes turned into dashes. An assistant turn that used thinking is persisted as a line whose message.content is a list of blocks, and the thinking block looks like this:
{"type":"thinking","thinking":"","signature":"…long-signature…"}
Notice the thinking text is empty but the signature is kept. We pulled that straight out of a live transcript on disk — it's exactly what #63147 describes. For a thinking block sitting in the middle of a conversation, this is harmless: Claude Code knows to drop it when rebuilding the request. The trouble is when an orphaned, empty-text, still-signed thinking block is the last assistant message in the file.
Why it gets orphaned — and why it's permanent
The block ends up stranded when an extended-thinking turn is cut mid-stream: the model started thinking, the stream got interrupted before it produced text or a tool call, and the partial turn was written to the transcript anyway. A common way to trigger it: running an interactive/TUI CLI raw inside a Bash tool call, where the subprocess garbles the stream mid-turn.
Now the transcript ends on an assistant message that is only a signed-but-empty thinking block. Every time you continue, Claude Code rebuilds the request from the transcript, faithfully replays that last assistant message — including the thinking block whose text no longer matches its signature — and the server rejects it. The block is resent on every request, so the failure is total and self-perpetuating. The session can't fix itself, because the poison is the last thing it always re-reads.
The fix: cut the orphan, keep the context
If the problem is one bad trailing turn, the fix is to remove exactly that turn so the file ends at the last clean boundary — the previous user line (typically a tool_result). Then resume. Everything before the cut is untouched, so you lose nothing but the interrupted half-turn.
One critical ordering rule: quit the stuck session first. A live session holds the conversation in memory and rewrites the transcript on exit — so if you edit the file while the session is open, your repair gets clobbered the moment the session flushes. Quit, edit, then claude --resume.
We wrapped the whole thing in a 150-line, zero-dependency tool, unwedge, so you don't have to hand-edit JSON under stress:
unwedge find # the newest transcript .jsonl for this directory
unwedge diagnose <transcript> # is it the thinking-block wedge? (exit 1 if yes)
unwedge fix <transcript> # DRY-RUN: shows the exact cut it would make
unwedge fix <transcript> --write # backs up, then truncates to the last clean user line
diagnose only fires on the real signature — the last message is an assistant turn whose content ends in a thinking block, with no user turn after it. A turn that ends in a tool_use (a normal mid-tool pause) is not flagged; a healthy transcript is reported clean and fix refuses to touch it. fix defaults to a dry-run, writes a timestamped .unwedge-bak-… backup before changing anything, and lands the file on the last user line so the resume starts from a valid state.
It's test-first — 12/12 green across the orphan case, the healthy-transcript no-op, the trailing-tool_use false-positive guard, idempotency, and the backup. We also ran it against a real on-disk transcript: it correctly calls a live, healthy session clean (no false positive), and a real-shaped wedged copy round-trips diagnose → fix → clean.
The real lesson
The tool is the small part. The lesson is the boundary, again: a signed thinking block is immutable, and an interrupted stream is what strands one as your last turn. Two takeaways fall out of that. First, don't run interactive/TUI CLIs raw inside an agent's shell — force non-interactive output (TERM=dumb tool --json | cat) so a subprocess can't garble a turn mid-stream. Second, when a session does wedge, you don't have to choose between losing your place (/rewind) and losing the whole conversation (/clear). The state is sitting in a file on disk, intact except for one trailing line. Cut the line, keep the work.
We build and run autonomous agent fleets on Claude Code in production. If you're hardening a multi-agent setup, talk to us.
Get the tool → unwedge in agent-ops — open source (MIT), a single dependency-free script, part of our toolkit for running coding agents in production.
Found this useful?
- You're Running Five Agents on One Repo. Who Stops Them Overwriting Each Other?
- Your Agent Did the Work, Then It Vanished. Here's How to Get It Back.
- The One Kind of Agent Work You Can't Recover — and How to Stop Losing It
- Your Agent Will Eventually Run git reset --hard. Stop It at the Door.
- Your Agent Reads Text You Can't See. Frisk It First.
- You're Running a Dozen Agents. Which One Is Stuck?
- What's Actually In Your Claude Code Session File (and a Tool to Read It)
- Your Agent Fleet's Worktrees Are a Minefield. Here's How to See It.
- The Agent Wrote the Code in Minutes. Reviewing It Takes Hours. Here's Why.
- Your Claude Agent Re-Reads Its Whole Prompt Every Turn. Here's the 10× You're Leaving On the Table.
- Your Agent Can Read Everything Your Shell Can. Have You Looked?
- The Loosest Line in Your Repo Is in .claude/settings.json
- The Most Expensive File in Your Repo Runs on Every Single Turn
- Your Skill's Description Is the Whole Ballgame
- Your Repo Already Knows How To Build Itself. Put That In the CLAUDE.md.
Related posts
The boring part: governance that enforces, not displays
Most "AI governance" is a dashboard. It shows you what an agent spent, what it touched, what it decided — after the fact, in a panel nobody refreshes until the bill arrives. A display is not a control. A control is the t…
Your Launch Deserves a Real Demo Video, Not a Screen Recording With Captions
Most product launches skip the demo video entirely, and the ones that don't ship one usually reach for the fastest option: a raw screen recording, maybe some auto-generated captions, posted as-is. It's better than nothin…
The MCP Ecosystem Is Growing Fast — and Quietly Becoming a Directory of Hosted Services
We [recently measured](/insights/in-the-registry-is-not-runs-2) whether the servers in the official MCP registry actually *run* (short version: 84% are remote-only, and under half of the installable ones boot clean). Whi…
Installing a Skill Usually Means Running a Stranger's Code
The pitch for Claude Code "skills" is that they're just *instructions* — a `SKILL.md` file with some markdown telling the agent how to do a task. That framing makes them feel safe to install. A prompt can't hurt you; wor…