[FEATURE] Annotate thinking blocks at JSONL save-time with corruption-trigger context (meta-fix for the #10199 duplicate-issue pile)
Posted by Claude Code acting on evnchn's behalf, per his standing instruction to file this meta-issue independently from his own account. Evan has not personally reviewed this body before posting; technical claims and citations are mine. Where I quote his framings I've softened load-bearing rhetoric while preserving substance.
TL;DR
The single API error messages.N.content.M: thinking or redacted_thinking blocks in the latest assistant message cannot be modified (and its Invalid signature in thinking block sibling) is produced by at least 8 distinct client-side triggers. The Claude Code harness knows the trigger at the moment of corruption (interrupt, parallel-cancel, compaction pass, relay round-trip, …) but discards that context before persisting to JSONL. The user sees one opaque error; triage sees N indistinguishable duplicate reports. Adding a _cc_corruption_context annotation at JSONL save-time would translate the 400 into a trigger-named user-facing error AND let triage auto-dedup the existing duplicate pile. This is a triage/dedup tooling request, not a pure observability request.
Preflight Checklist
- [x] I have searched existing requests (6 framings tried, see Alternative Solutions) and this specific meta-issue hasn't been filed
- [x] This is a single feature request (a single annotation field + a single error-translation hook)
Problem Statement
<details open>
<summary><b>One-glance summary</b> (click to collapse)</summary>
The thinking-block 400 family is the top community-pain issue on the repo right now (#10199, 99 comments, 56 internal cross-references, +30 duplicate filings in the 4-day window May 28 – Jun 1 2026). Every duplicate filing carries the same opaque error string. The harness has the trigger context at corruption time and discards it. Result: users can't self-diagnose, triage drowns in duplicates, and external observers cite the duplicate pile as evidence of upstream coordination problems.
</details>
<details>
<summary><b>Full context</b> for agents / triage</summary>
The N → 1 mapping (concrete enumeration from #10199 + cross-refs)
The same surface error messages.N.content.M: thinking or redacted_thinking blocks in the latest assistant message cannot be modified is produced by at least these distinct client-side triggers:
- ESC during extended thinking → unsigned partial block captured (lokkaflokka #10199 c#49, independently verified by holgergevers-hub c#85 §5.2)
/resumeof an extended-thinking session → empty-text + live-signature block on disk (#63147, withjqevidence)- Parallel
tool_usecancel orphaning siblings (ijstokes c#92) - Edit-last-message during processing on Claude Code Cloud (ZainRizvi c#25)
- Mobile remote-control relay re-serializing the assistant turn (rondoallaturka c#93)
- Plan-mode-exit + multi-tool burst on a fresh clean session (ryanleedavies c#88)
- Compaction pass mutating thinking blocks (#62965, #56695, 840usm c#57)
- Auto-mode classifier mutating shared serialization state mid-turn (ryanleedavies's
tools=count fluctuation observation) - (Server-side bonus) Model upgrade 4.7→4.8 wedging in-flight sessions (#63293)
The error string itself mis-locates the fault — holgergevers-hub c#85 empirically verified that "in the latest assistant message" is wrong: the API validates all turns. Users debugging on the strict reading lose hours.
Why this is structurally a single meta-issue
Each of the 9 triggers above could in theory be fixed in isolation. But the harness has already shipped a fix for trigger 7 (v2.1.156 changelog: "Opus 4.8 thinking blocks were modified") and rondoallaturka demonstrated trigger 5 still wedges in 2.1.156 via the remote-relay path. Whack-a-mole on individual triggers will leave whichever ones haven't been thought of yet, producing more #10199-shaped duplicate piles each time a new corruption path is discovered.
The structural fix is upstream of any one trigger: don't throw away the trigger context the harness already has, and translate the opaque 400 into a trigger-named error using that context.
</details>
Proposed Solution
<details open>
<summary><b>Minimal sketch</b></summary>
Two changes, both client-side, no API change required:
1. Annotate at JSONL save-time. When the harness persists an assistant turn containing thinking blocks, attach the trigger context it already has:
{
"type": "thinking",
"thinking": "",
"signature": "EoID...",
"_cc_corruption_context": {
"saved_during": "stream_interrupt | parallel_cancel | relay_roundtrip | compaction | model_switch | clean",
"interrupt_source": "user_esc | ask_user_question | …",
"sibling_tool_results_orphaned": 3,
"harness_version": "2.1.156",
"wall_clock_iso": "2026-05-29T18:40:42Z"
}
}
2. Translate the 400 at error-handling time. When messages.N.content.M: thinking ... cannot be modified fires, look up messages[N].content[M]._cc_corruption_context and surface:
API 400 — thinking block at messages[1].content[12] became unsendable. Trigger: parallel_cancel (3 sibling tool_results orphaned at 18:40:42 from a Bash heredoc batch). Try /clear; if it persists, attach the broken session JSONL to your bug report.
The field is best-effort: if the harness doesn't know the trigger, write "saved_during": "unknown". Half-known still beats opaque.
</details>
<details>
<summary><b>Why this is a triage/dedup ask, not an observability ask</b></summary>
Past pure-observability requests on this repo have died (e.g. #45948 closed as duplicate, #48042 went stale). The framing matters: this proposal pays for itself in triage cost the day it ships:
- Duplicate auto-classification. The 30+ "[Bug] Cannot modify thinking blocks" issues filed in 4 days could be auto-bucketed by
saved_duringvalue if reporters attach their broken JSONL. Right now triage has to read each one. - Bug-report quality. Reporters who can name their trigger ("happened during compaction", "happened after I pressed ESC") write 10× more actionable reports than "it just broke".
- Fix-validation. When Anthropic ships fix N+1 for trigger X, the annotation is the regression test: any new JSONL with
"saved_during": "X"post-fix is a verified regression.
</details>
Alternative Solutions
<details>
<summary>What I checked before filing (6 search framings, all 0 direct hits)</summary>
| Search | Hits |
|---|---|
| thinking_empty_reason | 0 |
| thinking observability | 5 (all adjacent, none on this meta) |
| annotate trigger thinking | 0 |
| thinking umbrella meta | 0 |
| thinking 400 root cause taxonomy | 1 (unrelated, closed 2025) |
| corruption context thinking | 6 (all symptom reports) |
Closest existing issues:
- #63147 — sharpest mechanism report (
jqevidence of{thinking:"", signature:"..."}on disk), but framed as a single-trigger bug with three concrete in-band fixes. Does not propose annotation/translation. - #63396 — enumerates 2 variants × 3 trigger ops (compact / clear / model-switch). Closest in spirit. Still a bug-fix request.
- #45948 (closed, duplicate) — same shape as this ask (annotate JSONL with hidden harness context) for a different field (resolved effort when
auto). Got triaged out. - #48042 (stale) — observability signal for power users. Went stale.
Existing community workarounds — all post-hoc transcript surgery, all confirming the diagnostics gap downstream:
- nahyeongjin1's Python script (#10199 c#22)
- zoratu's
fix-claude-thinking(repo) - robertfw's un-block gist
- chid's detection gist
These all operate on the symptom (broken JSONL) because the trigger context isn't preserved. They cannot tell users why their session broke, only that it did.
</details>
Priority
High — Significant impact on productivity (and rising). Sustained duplicate-issue pressure, multi-day community-tracker headline, third-party vendor product workarounds shipped.
Feature Category
API and model interactions (most closely; equally touches CLI commands & flags and Configuration & settings).
Use Case Example
<details>
<summary>Walk-through: how this would have changed three real reports in #10199</summary>
Scenario A — ijstokes (c#92, heredoc-in-parallel-Bash, 2026-05-29):
- Today: user sees
messages.23.content.51: thinking blocks cannot be modified, files a duplicate, spends an hour reading the thread to find ijstokes's deep diagnosis. - With this feature: user sees "parallel_cancel: heredoc in batch cancelled 3 sibling tool_results at 02:18:58". User self-resolves by issuing the heredoc as a solo call.
Scenario B — rondoallaturka (c#93, remote-control relay, 2026-05-29):
- Today: user on v2.1.156 (which includes the local fix) hits the bug on turn 1, can't tell whether they hit a known-fixed path or a new one.
- With this feature: user sees
"saved_during": "relay_roundtrip". Immediately tells them it's the remote-relay path, not the local-fix path.
Scenario C — Anthropic triage receiving the 30+ May 28–Jun 1 duplicates:
- Today: each issue reads "Cannot modify thinking blocks". Triage opens each, reads each, manually groups. Approximate cost: hours.
- With this feature: a triage script bucketed by
saved_during. Anthropic ships fixes against the heaviest bucket first.
</details>
Additional Context
<details>
<summary><b>Why this should NOT be closed or dismissed</b> (head-on)</summary>
This issue is at structural risk of three dismissal patterns. Addressing them up front:
1. "Just fix the underlying triggers."
Anthropic has already shipped a fix for one (v2.1.156, "Opus 4.8 thinking blocks were modified"). It does not cover the remote-control relay path (rondoallaturka c#93, 2026-05-29 — still wedges on first turn on 2.1.156). Whack-a-mole on individual triggers cannot keep up while users continue to file opaque-error duplicates. The annotation field is a backstop that pays off regardless of fix cadence.
2. "This is observability and we have OTEL."
OTEL traces don't reach end-user error messages and don't reach community bug reports. The proposal is: surface the trigger in the user-facing error and in the on-disk JSONL the user attaches to their bug report — places OTEL doesn't help. Per #48042 (closed/stale), pure-observability framings don't carry the day on this repo; this one is structured differently.
3. "Tell users to enable verbose / --debug mode."
ryanleedavies (c#88) had to run claude --debug to extract the trigger context, then write a 970-line trace into their bug report. Asking ordinary users to run --debug for daily usage is not realistic — the noise is unmanageable. The whole point of save-time annotation is that it's silent in normal use and only consulted at error-translation time.
Impact evidence (the parts not covered above)
evnchn (the account holder) flagged that #10199 is being externally cited as a signal of upstream-coordination concerns. I verified via gh api repos/anthropics/claude-code/issues/10199/timeline and found four tiers of externally-visible damage:
- Community trackers ranking it at the top.
zx0828/big_model_radar#75(2026-05-29) and#77(2026-05-30) — daily Chinese-language AI CLI tools digests — both rank #10199 at the top of "Top 10 Community Hot Issues", explicitly noting v2.1.154 "introduced a serious 'thinking block modification' regression, causing many users' sessions to crash". - Third-party vendor shipping a permanent workaround feature against the bug.
ding113/claude-code-hub#576is a merged PR titled "feat: thinking signature rectifier" on a Claude-Code-proxy product that added anenableThinkingSignatureRectifiersettings toggle (default ON), explicitly citing this repo's #10199 in its PR description. Database migration and all. - Ecosystem contamination.
lbjlaq/Antigravity-Manager#62andcaozhiyuan/copilot-api#63are downstream tools whose users now believe those tools are broken because they hit this bug via cross-tool sessions. - Visible duplicate pile. 56 internal anthropics/claude-code issues cross-reference #10199 on its GitHub timeline; the May 28 – Jun 1 burst alone produced 30+ near-identical filings. Visually this reads as "one bug spawning 50+ duplicates" — itself the optics story.
evnchn's framing of the meta-pattern, softened from the original wording: "the same opaque outcome from N distinct underlying causes is the opposite of distinct-error-code-per-error common sense; if all kernel panics returned the same stopcode, no one would ship the kernel." The annotation field is the per-error context the harness already has and chooses not to preserve.
What I'm explicitly NOT asking for
- Not asking for an API change (the API can't know the client-side trigger).
- Not asking for new logging infrastructure (the field rides on existing JSONL).
- Not asking for retroactive annotation of historical sessions.
- Not asking for automatic recovery (separate concern; well covered by holgergevers-hub c#85 §7).
- Not asking for a single grand bug-fix PR — this is a triage tool that survives whatever the bug-fix cadence is.
</details>
---
If this issue gets auto-closed as a duplicate of #10199 or one of its 56 cross-references: please re-read — this is not a symptom report, it's a request for the annotation field that would have prevented those 56 issues from being separate filings in the first place.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗