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

Status Open
Maintainer reply None cached
Activity 3 comments · opened Jul 23, 2026

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 .jsonlctrl+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 .jsonl and not recoverable via ctrl+o.
  • Signature in the transcript right before a popup/tool call: only adjacent thinking → thinking → tool_use entries remain; the narrative text blocks 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 → default setting 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.

View original on GitHub ↗

3 Comments

holygege40 · 1 month ago

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 assistant rows by requestId (Claude Code splits one API response across multiple .jsonl lines, so per-line counting skews the numbers), then flag any group that contains an AskUserQuestion tool_use block but whose total text block 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:

  • 61 × thinking → tool_use
  • 51 × tool_use (no thinking block persisted either)
  • 24 × thinking → text → tool_use where the text is under 50 chars
  • 2 × text → tool_use

So 112 of 138 have no text block 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 claude binary and injects its own hooks via --settings). Three independent reasons it is not the cause:

  • cmux was installed 2026-07-19 18:37. The 11 sessions from 7-19 to 7-21 (v2.1.214–216) have a 0% failure rate. The failure begins on 7-22 with the v2.1.217 upgrade, three days after the wrapper appeared.
  • The transcript .jsonl is written by the CLI process itself. A PTY wrapper cannot remove a text block from a file it does not write.
  • Failure rate tracks the client version, not the front-end, across the whole 140-session history.

Also worth noting for anyone else on a wrapper: cmux's --settings injection merges with user settings.json rather than replacing it — my own PreToolUse mitigation hook kept firing normally.

5. Why the v2.1.220 number is deflated

Since 2026-07-23 I run a PreToolUse hook on AskUserQuestion that 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_tokens against 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 by requestId disproved it.

---

Environment: Darwin 23.6.0 (arm64), Claude Code v2.1.220, claude-opus-4-8 and claude-opus-5, xhigh reasoning effort with interleaved thinking enabled, ~140 sessions sampled from a single project directory over 2026-06-26 → 2026-07-28.

alvarolb · 28 days ago

Confirming this on v2.1.220 as well, and adding a data point: it is model-dependent.

Setup

  • Claude Code v2.1.220, macOS
  • Interactive session (via a wrapper app that reads the session .jsonl; the drop is also visible in the CLI's own on-screen output, so it is not a wrapper artifact)
  • Hooks enabled here, but as you noted they are not the cause — the text never reaches the transcript

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 between Read calls). 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 thinking entries right where the missing text blocks 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.

holygege40 · 28 days ago

Re: model dependence — claude-opus-5 is affected too, and a single clean run is not evidence

@alvarolb thanks for the independent confirmation on v2.1.220. Your claude -p vs 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/5

Same 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 .jsonl by scanning assistant rows for text blocks.

M1: OK  rid=...hZwcdMap  len=102
M2: OK  rid=...JBbeCWhJ  len=158
M3: OK  rid=...7V5NcDCT  len=117
M4: OK  rid=...6oBu75MC  len=221
M5: OK  rid=...Yd46Nf3T  len=60
survived: 5/5

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 (group assistant rows by requestId; a turn containing an AskUserQuestion tool_use whose total text content 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 PreToolUse mitigation 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

  • Not specific to 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 plain date +%s calls and reproduced it anyway — same bug, wider blast radius than my numbers suggest.
  • Don't count markers in the turn you just emitted them in. The text row for the current block may not be flushed to the .jsonl when 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 --version2.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.