VS Code extension: mid-turn assistant text silently lost — recorded as empty thinking blocks, never reaches the UI (deeper than #24733)
Summary
Assistant text emitted between tool calls (mid-turn) is silently lost before it reaches the UI. The user never sees it, and — critically — the session JSONL shows the text was never persisted as a text content block at all: at the positions where the text should be, there are assistant entries containing only an empty thinking block ("thinking": "" with a signature).
This looks deeper than #24733 (text disappearing during streaming re-render): here the text never exists in the recorded conversation, so it is not a webview rendering problem. We verified the webview has no filtering logic that could drop it (see "What we ruled out").
The model, meanwhile, "believes" it emitted the text — its own context shows the message as sent — so it confidently relies on explanations the user never received. This caused real confusion in our sessions (the assistant asked a confirmation question via AskUserQuestion referring to an explanation that was never displayed).
Environment
- Claude Code CLI: 2.1.216
- VS Code extension: anthropic.claude-code 2.1.220 (win32-x64), chat panel (native extension surface)
- OS: Windows 11 Home 10.0.26200
- Model: claude-fable-5, extended thinking enabled
Reproduction (observed pattern)
- In the VS Code extension chat, give the assistant a task that involves several tool calls in one turn (e.g. a few Bash calls) with AskUserQuestion at the end.
- Have the assistant emit explanatory text between the tool calls (multi-line and/or markdown-formatted text is affected most reliably; occasionally a short single plain line does survive).
- Observe the chat: the interim text is not displayed.
- Inspect
~/.claude/projects/<project>/<session-id>.jsonl: the intended text is absent; in its place is an assistant entry whose content is a single emptythinkingblock.
We reproduced this dozens of times in one session with labeled probe messages (plain 1-line, 3-line, 6-line, 10-line, 1-line with inline markdown, paragraphs, tables, headings) and confirmed visibility ⇔ presence of a text block in the JSONL in every single case.
Evidence (session JSONL excerpt, probe messages only)
Probe "W1" (a single plain line) was displayed and is recorded as text. Probes "W2" (3 plain lines), "W3" (6 plain lines), "W4" (1 line with bold/code/link) were generated by the model in the same turn between Bash calls, were NOT displayed, and are absent from the JSONL — the entries at their positions are empty thinking blocks:
line 522 (assistant): [{"type":"text","text":"W1テスト:これは1行だけの平文です…"}] <- displayed
line 523 (assistant): [{"type":"tool_use","name":"Bash"}]
line 525 (assistant): [{"type":"thinking","thinking":""}] <- W2 should be here; not displayed
line 526 (assistant): [{"type":"tool_use","name":"Bash"}]
line 529 (assistant): [{"type":"thinking","thinking":""}] <- W3 should be here; not displayed
line 530 (assistant): [{"type":"tool_use","name":"Bash"}]
line 532 (assistant): [{"type":"thinking","thinking":""}] <- W4 should be here; not displayed
line 533 (assistant): [{"type":"tool_use","name":"AskUserQuestion"}]
The full strings of W2/W3/W4 do not appear anywhere in the JSONL (neither in text nor inside any thinking content).
Text at the very start of a turn (before the first tool call) and the turn-final message are reliably persisted and displayed, regardless of length or formatting.
What we ruled out (static analysis of the extension bundle)
We inspected anthropic.claude-code-2.1.220-win32-x64 (extension.js, webview/index.js, minified):
- The extension host forwards messages to the webview unfiltered (
postMessage({type:"from-extension", message})). - Assistant content ingestion drops only
type:"fallback"blocks;text/thinking/tool_useare all kept. - An assistant message is hidden only when all of its content blocks are UI-hidden
tool_useblocks; anytextblock forces rendering. - The only content-dependent rendering behavior found is a temporary hold of the trailing paragraph while
isPartialText(streaming) — nothing that would permanently drop text by length, markdown, or position.
So the loss happens upstream of the extension UI: the intended user-visible text is emitted (or recorded) as an empty thinking block instead of a text block.
Expected
Text the assistant emits between tool calls is persisted as a text content block and rendered in the chat (or, if intentionally suppressed, at least the model should be made aware its interim text is not user-visible).
Actual
Mid-turn assistant text is usually lost end-to-end (not displayed, not in the session record), replaced by an empty thinking block. Short single plain lines occasionally survive, which makes the behavior look nondeterministic to users.
Impact
- Users miss explanations, warnings, and the context for AskUserQuestion confirmations.
- The model's context still contains the "sent" text, so it acts as if the user read it — a silent divergence between what the model believes it communicated and what the user saw.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗