Opus 5: prose between tool calls returned as summarized 'narration' thinking blocks since 2026-09-11 — code blocks dropped, Desktop never shows them, model believes user saw it
Summary
Since 2026-09-11 ~02:00 UTC, on claude-opus-5 (effort xhigh), the prose Claude writes between tool calls (after a tool_result, before the next tool_use) frequently comes back not as a text block but as a thinking block containing a server-written summary (the block the stream-json schema calls a narration block: "server summaries of the prose between tool calls, not the model's own reasoning"). Three consequences compound into a user-visible bug:
- The summary rewrites the wording and drops fenced code blocks. A command the model wrote for the user disappears.
- The Desktop app does not show these blocks at all (the CLI renders them as ordinary text). Two real sessions in the Desktop app: the user asked "when did you ever tell me that?" after the model's instructions existed only in a narration block.
- The model still has that narration in its context and believes the user saw it, so it finishes the turn with "run the command above" / "as I explained above" — while nothing was ever shown. The
turn_updatessystem-prompt variant that warns the model ("Text you write between tool calls may not be shown to the user … restate it in the final message") is not active for this account.
No client or settings change happened at onset. Same onset time as #93596 ("Opus 5 at xhigh … since ~02:30 UTC 11 Sep, with no client or settings change").
Environment
- Claude Code CLI 2.1.266 → 2.1.278 (macOS, Darwin 27.0.0); Claude Desktop 2.2553.1 (bundles Claude Code 2.1.271 / 2.1.275)
- Model
claude-opus-5[1m], effortxhigh, adaptive thinking (defaults), first-party claude.ai login, auto permission mode showThinkingSummariesunset,CLAUDE_CODE_THINKING_DISPLAY_UPDATESunset
Evidence (from ~/.claude/projects/**/*.jsonl, 60 days, ~57k assistant messages)
Block shape of a continuation response (the response that follows a tool_result), one message.id:
| | shape |
|---|---|
| healthy | thinking("" + signature) → text → tool_use |
| affected | thinking("" + signature) → thinking(summary prose + signature) → tool_use (no text at all) |
Continuation responses that contain a tool call, by day (first responses of a turn are never affected):
| day | Claude Code | cont. narration as text | as narration thinking | narration % |
|---|---|---|---|---|
| 09-07 | 2.1.260 | 485 | 0 | 0 |
| 09-08 | 2.1.260 | 986 | 36 | 4 |
| 09-09 | 2.1.260 / 2.1.267 | 581 / 10 | 16 / 0 | 3 / 0 |
| 09-11 | 2.1.266 | 37 | 141 | 79 |
| 09-12 | 2.1.266 | 8 | 122 | 94 |
| 09-14 | 2.1.270 | 32 | 216 | 87 |
| 09-16 | 2.1.271 | 408 | 266 | 39 |
| 09-17 | 2.1.271 | 426 | 228 | 35 |
| 09-21 | 2.1.275 / 2.1.278 | 93 / 0 | 57 / 13 | 38 / 100 |
The flip is by date, not client version (2.1.267 on 09-09 is clean; 2.1.266 on 09-11 is 79 %). No CLAUDE.md / hooks / settings changes on 09-10/09-11 (git history of ~/.claude).
Content of the narration blocks is a paraphrase of what the model wrote (e.g. my own text "거친 정규식이어서 잡음이 섞였지만 … 두 건을 상세히 봅니다" was stored as "정규식이 거칠어서 잡음이 섞였지만 … 두 건을 자세히 살펴보겠습니다"), written in first person, sometimes in past tense describing the model's own output ("I explained two ways to copy the secret value…" — while no text block exists).
Incident A (Desktop, 2026-09-16)
- Model runs
git worktree remove …via Bash → denied by the auto-mode classifier. - Next response:
thinking("")+ narrationthinking: "…함장님이 터미널에서 제시된 명령어를 직접 실행해주셔야 합니다" ("you must run the command I presented in your terminal") +AskUserQuestionwhose option description says "run the command above". - Final text: "위 명령을 터미널에서 실행하시면 …" ("if you run the command above…"). No
textblock in the whole turn contains the command; it only ever existed in the denied Bashtool_useinput and in the narration block. - User: "아니 명령어를 또 안 줬네?" ("you didn't give me the command again").
Incident B (Desktop, 2026-09-21)
Narration block (no text block in that response): "Google 콘솔에서 시크릿 값을 복사하는 두 가지 방법(터미널 명령 또는 콘솔에서 직접 확인)을 안내해 드렸습니다" ("I have explained two ways…") → AskUserQuestion. User's answer: "네가 언제 안내해줬어?" ("when did you explain that?"). Next response again narration-only: "안내가 모달에 가려 안 보였을 수 있어 다시 정리했습니다" ("I restated it") — again no text block. The gcloud … | pbcopy command reached the user only on the third attempt, as a text block after the user typed "터미널 명령어를 출력하라고".
Controlled repro (claude -p … --output-format stream-json --verbose, 2.1.278)
Prompt asks for a sentence + a fenced echo HELLO-MARKER-42 before the second tool call.
- Run 1 (defaults): the pre-tool prose came back as
thinkingwithnarration_block_indexes: [0];HELLO-MARKER-42appears in notextblock — the fenced command was summarized away. - Run 2 (defaults): same prompt, prose came back as
text, marker intact → the conversion is probabilistic (matches the 35–60 % above). - Run 3 (
CLAUDE_CODE_THINKING_DISPLAY_UPDATES=false):text, marker intact, no narration blocks (n=1, so not proof; the code path suggests it disables thedisplay: "updates"request).
What I found in the client
- 2.1.278 requests
thinking.display: "updates"with betathinking-display-updates-2026-08-18by default (connector_textmode;CLAUDE_CODE_THINKING_DISPLAY_UPDATES ?? true). - The stream-json schema documents
narration_block_indexes: "indexes … of the thinking blocks whose signature the server tagged block_kind 'narration' (server summaries of the prose between tool calls, not the model's own reasoning), so a renderer can label them as summaries". - The platform docs ("Progress updates between tool calls") say under
display: "updates"progress-update blocks come back as "Summary text", and list only Fable 5.1 / Mythos 5.1 / Fable 5 — Opus 5 started producing them on 09-11. - Two system-prompt variants exist: one tells the model "Your text output is what the user reads between tool calls"; the other (
turn_updates) says "Text you write between tool calls may not be shown to the user … restate it in that final message". This account gets the former.
Expected
- Anything the model writes for the user is either shown verbatim or the model is told it will not be shown. Today neither holds in the Desktop app: the prose is summarized (code blocks lost), the summary is not rendered, and the model is told the opposite.
Asks
- Desktop: render narration blocks (as the CLI does), or do not request
display: "updates"for Desktop sessions. - Server: when summarizing prose between tool calls, keep fenced code blocks verbatim (or pass the prose through unchanged when it contains a fence).
- Client: whenever
display: "updates"is requested, also send theturn_updatesguidance so the model restates deliverables in its final message. - Document
CLAUDE_CODE_THINKING_DISPLAY_UPDATESas the opt-out if it is one.
Workaround in use
Two local hooks: a PreToolUse(AskUserQuestion) hook that denies questions referring to "the command above" unless the fenced command is inside the question field, and a Stop hook that blocks a final message which points at an earlier command without restating it in a fence. Replayed over 30 days of transcripts: 27/924 and 1/913 hits, both real incidents caught.
3 Comments
Same on claude-opus-5-5 (Claude Code 2.1.280, Desktop app, Code tab, Windows 11). The text written between tool calls never reached me, including twice when it held fenced code blocks I had asked for. Only the text that ended each turn was shown, and the model then said it had already posted the content.
Same on
claude-opus-5-5, Claude Code VS Code extension 2.1.280 to 2.1.283, Windows 11, subscription. Three things I have not seen in this thread yet, and one ask.1. The summary can change the facts, not just shorten them. The transcript keeps only the summary, but the model still has its original between-tool line in context and can say what it wrote. One line said the model was Opus 5.5 and that Anthropic's thinking docs list this behaviour, which they do: "Progress updates between tool calls" at https://platform.claude.com/docs/en/build-with-claude/thinking#progress-updates names Opus 5.5. The summary I was shown in its place said "Opus 4.5" and "undocumented". So the replacement is not only lossy, it can be wrong.
2. Answers that were lost, from my own transcripts, 20 to 27 Sept 2026. Of 796 Opus 5.5 replies that ended a turn, 150 contained at least one narration block (the signature contains
narration, as @aultra described). I picked out the ones whose final message was under 150 characters, on the guess that a message that short was unlikely to repeat what was swallowed. There were 6. I read all 6 by hand. In 4, something I needed was only in the swallowed text: three direct answers to my questions and one finding, and each time the final message referred back to it as if I had read it. In the other 2, the final message did carry the gist. I have not read the other 144, so 4 is a floor, not a rate.3. A Stop hook that catches the short-final case, as a stopgap. It reads the transcript, checks each thinking block's signature, and if the reply now ending holds a
narrationblock and its final text is under 150 characters, it blocks the stop once and quotes the summaries back (the transcript's thinking field holds the summary that was shown), so the model restates what was lost in one short message. It cannot catch a lost answer followed by a long final message that happens not to repeat it. The core:Scope it to blocks after the last finished reply and after the last real user message, or it re-judges text that was already delivered. Honour
stop_hook_active.The ask: please give users a per-session or settings opt-out of the between-tool summarising, or enable the
turn_updatesprompt variant mentioned above for every account whose between-tool text is summarised. The model currently believes its between-tool text reached the user, and on Opus 5.5 it sometimes did not.Drafted with Claude Code, checked by me.
Follow-up on the
CLAUDE_CODE_THINKING_DISPLAY_UPDATESopt-out, plus two things that matter for anyone building a repro. Claude Code CLI 2.1.286 (terminal, macOS),claude-opus-5-5[1m], first-party login. Narration blocks counted from~/.claude/projects/**/*.jsonlthe same way as above (thinkingblock whose base64-decoded signature containsnarration); "later batches" means tool-call responses after the first one in a turn, since the first is never affected.1.
CLAUDE_CODE_THINKING_DISPLAY_UPDATES=falsedid not stop narration blocks on 2.1.286.Set via
"env": { "CLAUDE_CODE_THINKING_DISPLAY_UPDATES": "false" }in~/.claude/settings.jsonbefore the session started. The Bash tool's shell inside that session sawCLAUDE_CODE_THINKING_DISPLAY_UPDATES=false. In the 2.1.286 binary the variable is read through a tri-state boolean parser that accepts"false"as false, so this is not a "stringfalseis truthy" problem as far as I can tell.With it set, the session still produced narration blocks:
| turn | later batches | narrated |
| --- | --- | --- |
| 1st (ordinary work) | 2 | 2 |
| 2nd (scripted test calls) | 5 | 1 |
| 3rd (scripted test calls) | 10 | 0 |
So the single clean run with
=falsein the original report was most likely chance (see point 3 for why scripted repros come out clean so often). I have not captured the outgoing request, so I can't say whether the client still sentdisplay: "updates"or the server produced the blocks regardless.2. On
claude-opus-5-5almost every working turn is affected.All turns since 2026-09-23 with at least 4 later batches, 2.1.280 to 2.1.294:
claude-opus-5-5: 307 of 318 turns had at least 40 % of later batches narrated, most of them 80–100 %. The 11 exceptions sit at 0–33 %.claude-opus-5(5 turns) andclaude-sonnet-5(2 turns): 0 narration blocks. Small sample, and the report above shows Opus 5 affected, so I'd read this as "not in my data", not "not affected".Batch size (1, 2 or 3 parallel calls), prompt length, a pasted prompt, Bash share, and whether the line starts with a label like
Trial 3:made no visible difference.3. Scripted "do some harmless tool calls" turns are mostly not narrated, which makes repros look clean.
Of the 11 Opus 5.5 turns that came in under 40 %, 4 were deliberate test turns of trivial read-only commands (
date,uname,wc -l, …) with a label in front of each line, including two runs in a fresh session with the opt-out removed (1 of 11 and 0 of 11 narrated). Ordinary working turns in the same sessions, with the same settings, were narrated at 80–100 %. Anyone testing a fix or an opt-out with a scripted prompt should expect a clean result whether or not the fix works, and should measure on real working turns instead.Also seen, consistent with the earlier comment about facts changing: the summary of "the variable is already in the env block" came out as "I've added CLAUDE_CODE_THINKING_DISPLAY_UPDATES=false to the env block", and one summary kept the original's distinctive nouns, so it looked verbatim on screen even though it was a narration block. Counting from the transcript is more reliable than watching the screen.
Drafted with Claude Code.