[Bug] Thinking blocks corrupted during message serialization for API request causes permanent session failure

Resolved 💬 3 comments Opened Feb 17, 2026 by mikatai Closed Feb 21, 2026

Bug Description
Bug: Session permanently broken — thinking blocks modified during message serialization for API request

Long session (~200 messages, many from run_in_background task-notifications). AutoCompact was disabled (autoCompactEnabled: false). After a successful assistant turn, a late task-notification from a completed background shell arrived. The next API call fails with:

API Error: 400 messages.5.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 is permanently unrecoverable — every subsequent attempt hits the same error because the corrupted conversation state is persisted in the JSONL. Tested 4 times, also tried /doctor. Had to abandon the session.

Analysis of root cause:

This is not the compaction pipeline (Microcompact/Session Memory Compact/Full Compact via Z90()/a8T()) — that was disabled. The bug is in the message serialization layer that builds the API request payload.

With 200+ messages and extended thinking enabled, CC must fit the conversation into the context window. The _e() status check detects the conversation exceeds limits. When building the API request, CC trims or restructures messages to fit — but in doing so, it modifies thinking blocks that carry cryptographic signatures. The API validates these signatures and rejects the request.

The error references messages.5.content.1 — message index 5 in the API payload, content block index 1. This means ~200 JSONL messages were compressed down to ~10 for the API call, and the 6th message's thinking block was altered during that process.

Two issues:

  1. Thinking blocks must be treated as opaque, immutable blobs during request building. They should either be kept byte-for-byte identical or the entire message should be dropped. Never truncate, reformat, or re-serialize a thinking block.
  2. No recovery path exists. Once the corrupted state is in the JSONL, every retry rebuilds the same broken request. CC should detect this 400 pattern and either (a) retry without the offending thinking block, (b) drop the message containing it, or (c) offer to fork the session from before the corruption point.

Environment: Version 2.1.42, macOS, extended thinking enabled, autoCompact disabled. Trigger was a task-notification arriving after a long session with many background tasks.

Environment Info

  • Platform: darwin
  • Terminal: iTerm.app
  • Version: 2.1.42
  • Feedback ID: 484f5b17-83e9-4f8b-bbad-e9fb86f676cb

Errors

[{"error":"Error: LSP server plugin:pyright-lsp:pyright failed to start: Executable not found in $PATH: \"pyright-langserver\"\n    at start (/$bunfs/root/claude:2334:22315)\n    at async H (/$bunfs/root/claude:2334:24763)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-17T17:03:03.478Z"},{"error":"Error: Executable not found in $PATH: \"pyright-langserver\"\n    at spawn (unknown)\n    at spawn (node:child_process:667:35)\n    at spawn (node:child_process:14:39)\n    at start (/$bunfs/root/claude:2334:20845)\n    at H (/$bunfs/root/claude:2334:24763)\n    at _ (/$bunfs/root/claude:2334:32531)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-17T17:03:03.478Z"},{"error":"Error: Failed to start LSP server plugin:pyright-lsp:pyright: Executable not found in $PATH: \"pyright-langserver\"\n    at <anonymous> (/$bunfs/root/claude:2334:32553)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-17T17:03:03.479Z"},{"error":"Error\n    at <anonymous> (unknown)\n    at onData (node:child_process:153:110)\n    at emit (node:events:95:22)\n    at addChunk (internal:streams/readable:264:47)\n    at readableAddChunkPushByteMode (internal:streams/readable:242:18)\n    at handleArrayBufferViewResult (internal:streams/native-readable:79:16)\n    at <anonymous> (internal:streams/native-readable:43:68)\n    at processTicksAndRejections (unknown:7:39)","timestamp":"2026-02-17T17:03:04.910Z"},{"error":"Error: Request was aborted.\n    at g5R (/$bunfs/root/claude:869:100994)\n    at next (native:1:11)\n    at jeB (/$bunfs/root/claude:5566:5182)\n    at next (native:1:11)\n    at xeB (/$bunfs/root/claude:5571:9101)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-17T17:04:09.877Z"},{"error":"Error: File does not exist.\n    at call (/$bunfs/root/claude:1309:6225)\n    at async <anonymous> (/$bunfs/root/claude:5821:1670)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-17T17:05:01.883Z"},{"error":"Error: File does not exist.\n    at call (/$bunfs/root/claude:1309:6225)\n    at async <anonymous> (/$bunfs/root/claude:5821:1670)\n    at processTicksAndRejections (native:7:39)","timestamp":"2026-02-17T17:05:01.886Z"},{"error":"Error: File does not exist.\n    at call (/$bunfs/root/claude:…

Note: Content was truncated.

View original on GitHub ↗

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