Assistant text between tool calls is silently lost: not rendered, not in Ctrl+O, not persisted to session .jsonl (interleaved thinking)
Environment
- Claude Code v2.1.210, CLI TUI in macOS Terminal (Darwin 25.5.0)
- Model:
claude-fable-5(produces interleaved thinking blocks) - Session type: claude.ai/code-connected session; evidence below is from the local session
.jsonlunder~/.claude/projects/
Summary
When the assistant emits a text block followed by tool_use blocks in the same response, and the response also contains interleaved thinking blocks, the text block is silently lost: it is (a) never rendered in the main conversation view, (b) not visible in the Ctrl+O transcript viewer, and (c) absent from the session .jsonl on disk — verified with both raw grep and JSON-decoded search (jq '[.. | strings] | join(" ")'), so it is not an encoding/escaping artifact. This is distinct from the known AskUserQuestion overlay family (#23862, #30422, #58207), which is display-layer occlusion of the last line; here the content is not persisted at all, so the user has no way to ever see it and --resume from the local file permanently drops it from context.
Worst case observed: a ~1,800-char findings message written immediately before an AskUserQuestion call was completely lost — the user answered the multiple-choice question without ever seeing the analysis it was based on, and only noticed when the assistant later referred back to "the message above the question".
Evidence (sanitized structure dump of one session)
Per-record content-block types extracted with:
jq -r 'select(.type=="assistant") | .timestamp + " " + ([.message.content[]? | if .type=="tool_use" then "tool_use(" + .name + ")" elif .type=="text" then "text[" + (.text|length|tostring) + " chars]" else .type end] | join(", "))' session.jsonl
In a ~1h session the model emitted roughly 30 mid-turn narration text blocks (confirmed live on screen-side context; the assistant restated several of them later, and the API turn continued normally). Only 11 text records exist in the .jsonl. Every dropped text shares one structural signature — the file shows two consecutive thinking records written milliseconds apart, then the tool_use(s), with the text gone:
Dropped example 1 — the lost ~1.8k-char message directly before AskUserQuestion:
2026-07-15T00:36:58.437Z thinking
2026-07-15T00:36:58.465Z thinking <- 28 ms apart
2026-07-15T00:37:06.055Z tool_use(AskUserQuestion)
Dropped example 2 — narration before an Edit batch:
2026-07-15T00:36:01.457Z thinking
2026-07-15T00:36:01.459Z thinking <- 2 ms apart
2026-07-15T00:36:05.224Z tool_use(Edit)
2026-07-15T00:36:10.445Z tool_use(Edit)
2026-07-15T00:36:20.440Z tool_use(Edit)
Survivor — a mid-turn text that DID persist; note the single thinking record directly before it:
2026-07-15T00:17:38.440Z thinking
2026-07-15T00:17:38.492Z text[37 chars]
2026-07-15T00:17:43.001Z tool_use(Edit)
2026-07-15T00:17:59.316Z tool_use(Edit)
Reliably persisted in every turn: the first text block of the turn (before any tool call) and the turn-final text block. Reliably dropped: mid-turn texts whose response carries an adjacent interleaved thinking block (the dominant case with this model).
Impact
- Users act on
AskUserQuestiondialogs without the analysis written for them, and cannot recover it via Ctrl+O (the transcript viewer reads the same incomplete file). - Local
--resumereconstructs context without these blocks — permanent context loss, not just a rendering gap.
Repro (best effort)
- Claude Code v2.1.210 with a model that produces interleaved thinking (e.g.
claude-fable-5). - Run a multi-step task so the assistant narrates between tool batches, ideally ending in an
AskUserQuestion. grepthe session.jsonlfor a distinctive phrase from the mid-turn narration → 0 matches; only turn-opening and turn-final texts are present.
🤖 Generated with Claude Code