[BUG] Assistant text silently dropped from transcript (data-layer, not render) mid-turn — Opus 4.8 + interleaved thinking, v2.1.218 (regression of #41814 / #14694)
Summary
Mid-turn assistant narrative text is missing from the transcript itself, not merely unrendered. On Opus 4.8 with interleaved (extended) thinking, long assistant text that is followed by more thinking/tool_use blocks in the same turn never appears on screen and is absent from the session .jsonl — ctrl+o (view transcript) cannot recover it either. This is distinct from the render-only reports in #41814 / #14694 / #14727, which were closed but describe a milder variant where the backend logs still hold the text.
Environment
- Claude Code: v2.1.218 (latest at time of report)
- OS: macOS (Darwin 23.6.0)
- Model: Claude Opus 4.8 (1M context) — model id
claude-opus-4-8[1m] - Reasoning effort: xhigh, interleaved / extended thinking enabled
- Terminal: system terminal (reproduced independent of hooks)
Symptom (user-facing)
User asks a question; the assistant appears to respond (turn completes, follow-up popups/tools fire) but no assistant text is shown. The model itself still "believes" it wrote the text (it will insist "the content is above"), because the text is still in the model's own context — but it is gone from both the screen and the transcript. Neither side is lying: the drop happens between generation and persistence.
Key finding — this is a DATA-LAYER drop, not a render bug
- The missing text is not in the session transcript
.jsonland not recoverable viactrl+o. - Signature in the transcript right before a popup/tool call: only adjacent
thinking → thinking → tool_useentries remain; the narrativetextblocks that were generated between them are simply absent. - Forensic example (one session): of 12 narrative text segments in a turn, only a single ~31-character short sentence survived; all longer segments were dropped.
Survival pattern (reproducible)
- Short text (≲30 chars) survives.
- Plain text placed as the very last block of a turn (no tool/thinking after it) survives.
- Long text followed by any further block (thinking or tool_use) in the same turn is reliably dropped.
Already ruled out
- Not autocompact / context pressure — reproduced at very low context usage.
- Not a hook interaction — reproduced with hooks disabled.
tui: fullscreen → defaultsetting change had no effect.- Reproduced across three separate real sessions.
Related (closed) issues — please consider this a regression / more severe variant
- #41814 (messages disappear from terminal after v2.1.89, macOS) — closed as completed for v2.1.89 / Opus 4.6, but the symptom persists on v2.1.218 / Opus 4.8.
- #14694, #14727 (v2.0.73, response text missing / first-last lines stripped) — those were treated as render-only; this report shows the transcript itself is missing the text.
Impact
Any substantive assistant output (analysis, plans, tables) that is followed by a tool call in the same turn can vanish with no recovery path, forcing a "write everything to a file" workaround. High-severity for interactive/agentic use on the current-gen model.
What would help you reproduce
Opus 4.8 + xhigh + interleaved thinking, then a turn where the assistant emits a long paragraph and immediately follows it with a tool call. Compare the on-screen output and the session .jsonl — the paragraph will be in neither. Happy to provide local session IDs privately (or via /bug telemetry) for correlation.
Suggested labels: bug, area:tui, platform:macos, has repro.
3 Comments
Follow-up: regression point is v2.1.217, still unfixed in v2.1.220 — and it is not a terminal/front-end issue
Ran a forensic sweep over 140 local transcript files in a single project dir. Methodology: group all
assistantrows byrequestId(Claude Code splits one API response across multiple.jsonllines, so per-line counting skews the numbers), then flag any group that contains anAskUserQuestiontool_useblock but whose totaltextblock content is under 50 chars. 580 qualifying turns.1. The regression starts at v2.1.217, not v2.1.218
| version | turns | no text block | rate |
|---|---|---|---|
| 2.1.205 | 42 | 3 | 7% |
| 2.1.207 | 33 | 5 | 15% |
| 2.1.210 | 11 | 0 | 0% |
| 2.1.211 | 9 | 0 | 0% |
| 2.1.212 | 11 | 1 | 9% |
| 2.1.214 | 1 | 0 | 0% |
| 2.1.215 | 14 | 0 | 0% |
| 2.1.216 | 4 | 0 | 0% |
| 2.1.217 | 20 | 18 | 90% |
| 2.1.218 | 46 | 38 | 82% |
| 2.1.219 | 30 | 17 | 56% |
| 2.1.220 | 63 | 17 | 26%* |
Aggregate: v2.1.193–216 = 48/421 (11%) vs v2.1.217–220 = 90/159 (56%).
\* The 2.1.220 figure is an undercount — see §4.
2. It is the client, not the model
Controlling for the model removes the obvious confounder. Same model,
claude-opus-4-8, across the version boundary:| model | version band | turns | no text | rate |
|---|---|---|---|---|
| claude-opus-4-8 | 2.1.193–216 | 409 | 41 | 10% |
| claude-opus-4-8 | 2.1.217+ | 66 | 56 | 84% |
| claude-opus-5 | 2.1.217+ | 93 | 34 | 36% |
Identical model, 10% → 84% purely across the client version boundary.
3. Block-sequence signature
Of the 138 failing turns:
thinking → tool_usetool_use(no thinking block persisted either)thinking → text → tool_usewhere the text is under 50 charstext → tool_useSo 112 of 138 have no
textblock in the transcript at all; the remaining 26 are truncated rather than fully dropped. This matches the original report — the model is not declining to write prose, the prose is not reaching the transcript.4. Ruling out the terminal / front-end layer
I run Claude Code inside cmux (an Electron wrapper that spawns the real
claudebinary and injects its own hooks via--settings). Three independent reasons it is not the cause:.jsonlis written by the CLI process itself. A PTY wrapper cannot remove atextblock from a file it does not write.Also worth noting for anyone else on a wrapper: cmux's
--settingsinjection merges with usersettings.jsonrather than replacing it — my ownPreToolUsemitigation hook kept firing normally.5. Why the v2.1.220 number is deflated
Since 2026-07-23 I run a
PreToolUsehook onAskUserQuestionthat blocks the call when the preceding prose is under 50 chars and forces the model to re-send. Every successful re-send adds a passing turn to the denominator. The 26% figure therefore measures "failures that survived a mitigation", not the raw rate. Uninstrumented, it is likely still in the 50–80% band consistent with 2.1.218/219.6. Dead end, documented so others skip it
Comparing
usage.output_tokensagainst the byte size of persisted content is not a usable signal for this bug. Healthy turns on v2.1.193 show gaps of 37k tokens, because unpersisted thinking is still billed as output. I nearly filed that as evidence before grouping byrequestIddisproved it.---
Environment: Darwin 23.6.0 (arm64), Claude Code v2.1.220,
claude-opus-4-8andclaude-opus-5, xhigh reasoning effort with interleaved thinking enabled, ~140 sessions sampled from a single project directory over 2026-06-26 → 2026-07-28.Confirming this on v2.1.220 as well, and adding a data point: it is model-dependent.
Setup
.jsonl; the drop is also visible in the CLI's own on-screen output, so it is not a wrapper artifact)Repro used (2 minutes, interactive only)
Ask the agent to emit 5 short numbered messages, each separated by a trivial tool call (
date +%s), then count how many of them exist in the session.jsonl.Important: this does not reproduce with
claude -p(7 clean runs with markers betweenReadcalls). It needs a real interactive session with interleaved thinking.Results (same CLI version, same session, same prompt)
| Model | Messages surviving in transcript |
|---|---|
|
claude-fable-5| 2 / 5 — only the 1st and the 5th (last block of the turn) ||
claude-opus-5(1M) | 5 / 5 |This matches the survival pattern in the original report: text followed by more thinking/tool_use in the same turn gets dropped, the last block of the turn survives. With Fable 5 I ran this exact 5-message form once (2/5) plus an earlier 4-marker variant in the same session that also dropped its two middle messages; with Opus 5, one run (5/5). Given the intermittency, treat the Opus 5 result as a strong hint rather than proof that it is unaffected.
Also observed in the affected turns: duplicated adjacent
thinkingentries right where the missingtextblocks should have been — consistent with a write/ordering race in the transcript writer rather than anything model-side.Why this hurts beyond the visible bug
In voice-driven workflows the assistant's mid-turn narration is the feedback channel: the user hears the first message of the turn and the final one, and nothing during several minutes of tool work. Worse, since the conversation history is rebuilt from the transcript, the dropped text never returns to the model's context either — the assistant believes it explained something the user never received, and neither side can tell.
Re: model dependence —
claude-opus-5is affected too, and a single clean run is not evidence@alvarolb thanks for the independent confirmation on v2.1.220. Your
claude -pvs interactive split is a useful narrowing. One correction on the model-dependence conclusion though, because the "Opus 5 = 5/5" data point is the exact trap I fell into early on.1. I ran your repro on
claude-opus-5[1m]— also 5/5Same setup: v2.1.220, interactive, interleaved thinking, five numbered text blocks each followed by a trivial tool call inside the same turn, then counted survivors in the session
.jsonlby scanningassistantrows fortextblocks.So your Opus 5 result replicates. It just doesn't mean what it looks like.
2. The aggregate says Opus 5 loses roughly one turn in three
Since my previous comment (2026-07-28) I have accumulated 85 new qualifying turns, all on v2.1.220, all
claude-opus-5, same methodology as before (groupassistantrows byrequestId; a turn containing anAskUserQuestiontool_usewhose totaltextcontent is under 50 chars = dropped):| window | model | version | turns | no text block | rate |
|---|---|---|---|---|---|
| 2026-07-28 → 08-03 (new) | claude-opus-5 | 2.1.220 | 85 | 24 | 28% |
| cumulative | claude-opus-5 | 2.1.217+ | 160 | 56 | 35% |
| cumulative | claude-opus-4-8 | 2.1.217+ | 64 | 54 | 84% |
| cumulative | claude-opus-4-8 | ≤2.1.216 | 197 | 11 | 6% |
At a 28% per-turn drop rate, a clean 5-for-5 run happens 19% of the time (0.72⁵). Two of us just drew that hand. Opus 5 is not immune — it fails less often than Opus 4.8, which makes single-session repro badly underpowered for this bug.
The refreshed per-version table (n for 2.1.220 has doubled since my last post, 63 → 130):
| version | turns | no text block | rate |
|---|---|---|---|
| 2.1.215 | 14 | 0 | 0% |
| 2.1.216 | 4 | 0 | 0% |
| 2.1.217 | 20 | 18 | 90% |
| 2.1.218 | 44 | 36 | 82% |
| 2.1.219 | 30 | 17 | 57% |
| 2.1.220 | 130 | 39 | 30%* |
\* still deflated by my
PreToolUsemitigation hook — every forced re-send adds a passing turn to the denominator. See §5 of my previous comment.3. Two methodology notes for anyone else measuring this
AskUserQuestion. My detector keys on it only because that is where the loss is user-visible (the model asks me to pick between options it never showed me). Your repro used plaindate +%scalls and reproduced it anyway — same bug, wider blast radius than my numbers suggest.textrow for the current block may not be flushed to the.jsonlwhen your tool call reads the file. My first pass scored the last marker as dropped; re-reading one tool call later showed it present. A false positive here is easy to publish by accident.4. Status
claude --version→ 2.1.220, unchanged for six days, still the latest. Regression point remains v2.1.217. Darwin 23.6.0 arm64, xhigh reasoning effort with interleaved thinking.Agreed on the impact framing: because the conversation is rebuilt from the transcript, dropped prose never re-enters the model's context either. The assistant genuinely believes it explained something the user never received, and neither side has any signal that it went missing.