Assistant text between tool calls is not rendered, not shown in ctrl-o, and not saved to the session file

Status Open
Reported on v2.1.205
Maintainer reply None cached
Activity 7 comments · opened Jul 8, 2026

This issue was written by Fable (Claude) working inside a Claude Code session, on behalf of Jeremy Howard, who will follow the thread.

What happens

When a response involves several tool calls, assistant text emitted between the calls disappears. Only two pieces of text survive: the text before the first tool call, and the final text that ends the turn.

In the default view, the whole run collapses to one line. Here is an actual six-call experiment:

⏺ Running the experiment now — six labeled variants, so you can report back which labels you can see. V1: plain one-line narration, straight into a kernel call.

Called clikernel 5 times, ran 1 shell command

⏺ Experiment complete. All six variants ran ...

In ctrl-o verbose mode, each tool call and its result render individually, but the assistant text between calls is still absent.

In the session .jsonl the text is not merely hidden: it was never written. We searched the session file for unique strings from each of the six narration messages, and none are present; the only matches are later messages that quoted them. The assistant text records that do exist are the message that opened the run, the turn-ending summary, and one message whose tool call was cancelled by a user interrupt, which made it turn-ending.

What we ruled out

The experiment sent six narration variants between 1+1 tool calls: a plain one-liner, multi-paragraph text, a markdown list, narration printed to stdout inside the tool call, narration before a switch from an MCP tool to built-in Bash, and narration after switching back. Formatting made no difference. Switching tool families made no difference: the Bash call folded into the same "Called clikernel 5 times, ran 1 shell command" line. Presence or absence of an extended thinking block made no difference. The stdout variant survives, but only inside the tool result, where nobody will look for narration.

Why this matters

The text between tool calls is where an agent says what the last result showed and why the next action follows. It is the user's only way to steer a long agentic run: catch a wrong turn early, contribute what they know, or stop wasted work. With this bug the user sees the opening sentence, a fold, and the conclusion, and the agent appears to have gone silent no matter how carefully it narrates. We spent real effort improving the assistant's narration discipline before discovering the interface was discarding the narration.

The persistence side is worse. The session file is the source for resume and for compaction, so the assistant's own record of why it did what it did is lost to any resumed session, and no verbose mode can recover it after the fact.

Environment

Claude Code 2.1.205, macOS, model claude-fable-5, tool calls via an MCP server (clikernel) and built-in Bash.

View original on GitHub ↗

3 Comments

mdickinson · 1 month ago

Seems similar to #66960

jph00 · 28 days ago

Additional characterization from controlled testing today (Claude Code 2.1.220, claude-fable-5 with interleaved thinking at high effort, macOS), building on the survival pattern in #80662 and the detection approach in podlayer/message-drop-sentinel:

Direction of the trigger. The fatal shape is text immediately followed by a thinking block. Text immediately preceded by thinking is unaffected: mid-turn texts of ~110 and ~570 chars in the shape thinking → text → tool_use (tool call directly after the text) persisted and rendered. This matches that repo's controlled pair (text → tool_use persists; text → thinking → tool_use drops).

Consecutive text parts drop as a unit, regardless of length. A single response containing three consecutive text parts — 15 chars, 340 chars, 14 chars — followed by a tool call persisted as just thinking → tool_use. All three texts vanished, including the two far below the ~30-char survival ceiling reported in #80662. Shortness protects isolated texts, but not members of a multi-text response.

This variant leaves no detectable scar. The known drop leaves two adjacent thinking blocks in the transcript (#77651). The multi-text drop above left a single thinking block — fewer thinking blocks than the generated sequence implies — so adjacency-based scar detection undercounts, and scar counts should be treated as a floor.

Hook and transcript visibility. The MessageDisplay hook event fires only for text that actually renders, so dropped texts produce no hook signal at any point. Separately, the session transcript JSONL on this machine flushes in batches minutes behind the live turn, so transcript-reading hooks (PostToolBatch, Stop) — and any immediate "did my text persist?" check — can read a stale prefix and mis-diagnose. We initially recorded a false "drop" this way; the text appeared on flush minutes later.

Practical mitigation that held up across a day of testing: anything the user must see goes in the turn-opening or turn-final message; a single mid-turn text part followed immediately by a tool call usually survived on this setup, but consecutive text parts never did.

misha-sunborn · 14 days ago

Confirming that this has been an issue for me for weeks. I work around it by making a rule for Claude to print a comprehensive final message. I use the Claude Code extension on VS Codium on Fedora 44.

Showing cached comments. Read the full discussion on GitHub ↗