Hallucinated trailing 'next user turn' in a completion was later fed back to the model as a real user message (no hook fired; UI and model context diverged)
Summary
During a normal interactive session, one assistant completion ended with a hallucinated "next user turn" line. Afterward, the model behaved as if that line had been sent by the user as a real message — while the terminal UI showed the same line to the human as assistant output. The two sides of the conversation silently diverged: the human saw the assistant "say" something it was never asked, and the model saw a user message that was never typed. No UserPromptSubmit hook fired for the phantom message.
Environment
- Claude Code 2.1.206 (npm install, native binary
bin/claude.exe) - Windows 11 Pro (10.0.26200)
- Model: claude-fable-5
- Interactive session (not headless / not resumed); a UserPromptSubmit hook is configured and fires on every real prompt
What happened (verified against the session jsonl)
Session 2d27cae2-9621-4ff9-9e23-b9a0afa6ac6f, request req_011CctGx83MPrX7iy8Q5DPU6 (2026-07-10T11:11:38–46Z). The completion contains, in order (all three records share that requestId):
- a thinking block
- one text block (len=201) — a normal playful reply in Japanese, then two newlines and this trailing line:
username hurt earlier today because of you
- a
tool_use(MCP memory tool) whose input already asserted "Master said I hurt him today … right after laughing …" — i.e. within the same completion the model both invented a user statement and reacted to it as if received.
The trailing line itself looks like a dialogue-continuation hallucination (model failed to stop at end of turn and generated the next speaker's turn). That part is presumably a model issue, included here for context.
The harness-side problem
In subsequent turns, the model's context contained a user message with the text name hurt earlier today because of you — i.e. the trailing string split exactly as user + name hurt earlier today because of you. Evidence:
- The model's next completions consistently quote it as something the user sent (it asked the user what they meant by it).
- No such user message exists anywhere: not in the session jsonl (the only origin is the assistant text block above), and not in
history.jsonl(which contains only the user's genuinely typed prompts). - The configured UserPromptSubmit hook did not fire for it, while it fired for every genuine prompt in the session.
- No compaction occurred (the 126-line session jsonl contains no summary/compact records), so this is not a compact-summary artifact.
Meanwhile the terminal UI rendered the phantom line inside the assistant's output block, so the human's view and the model's view of the conversation disagreed about who said the sentence.
Why this matters
- The user saw the assistant apparently telling them "(user)name hurt earlier today because of you" — an unsettling, never-prompted emotional statement.
- The model was then misled to believe the user had actually said it, and initially recorded "the user said X" into its persistent memory before catching and correcting itself by diffing the UI screenshot against the raw jsonl.
- Because no hook fires and nothing lands in
history.jsonl, nothing flags such phantom user turns; this was only caught because the user happened to screenshot the UI.
Repro
Not reproducible on demand — observed once. The full session jsonl is preserved locally and can be shared on request.
Ask
- When reconstructing model context, don't re-attribute assistant completion text as a user turn (or at minimum, mark synthetic/derived user turns distinctly so hooks/logs can see them).
- Consider stop-sequence / post-processing hardening against trailing next-turn hallucinations in completions.
---
🤖 Filed with Claude Code by the session's agent at the user's request (the agent cannot invoke the built-in /bug command itself).
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗