Phantom user message appeared in transcript without any user input
Phantom user message appeared in transcript without any user input
Description
During a long conversation (~120K+ tokens, v2.1.52, claude-opus-4-6), a type: "user" message appeared in the transcript with the content "Let me test if the blockquote ordering is correct".
I 100% confirmed that I never typed this message — not via keyboard, not via any external input method. This phantom message has occurred twice in this session. Every possible source of input has been ruled out.
Yet the UserPromptSubmit hook fired and received this prompt text via stdin. This means Claude Code itself internally generated a user message.
Timeline (from transcript)
[06:10:49Z] assistant: "Here's a summary of what I found..." ← normal response
[06:10:49Z] progress: Stop hook
[06:10:50Z] system/stop_hook_summary ← turn ends normally
[06:10:50Z] system/turn_duration (152372ms)
file-history-snapshot
← 93 second gap, no input
[06:12:23Z] user: "Let me test if the blockquote ordering is correct" ← PHANTOM
[06:12:28Z] assistant: [thinking] → responds to phantom message
[06:12:28Z] assistant: "The listener is running (PID 20288), go ahead and send a message to test."
The turn ends normally (stop_hook_summary + turn_duration), then after a 93-second gap with zero external input events, a user message suddenly appears. Claude then responds to it as if it were real.
Transcript raw data
// Turn ends normally
{"type":"system","subtype":"stop_hook_summary","hookCount":1,
"hookInfos":[{"command":"/Users/michael/bin/ccc hook-stop","durationMs":1211}],
"hookErrors":[],"preventedContinuation":false,
"timestamp":"2026-02-27T06:10:50.414Z"}
{"type":"system","subtype":"turn_duration","durationMs":152372,
"timestamp":"2026-02-27T06:10:50.418Z"}
// 93 seconds later — phantom user message appears
{"type":"user",
"message":{"role":"user","content":"Let me test if the blockquote ordering is correct"},
"userType":"external",
"timestamp":"2026-02-27T06:12:23.355Z",
"sessionId":"49cc5f89-ba00-44ca-856e-cd9f4382dfee",
"version":"2.1.52",
"permissionMode":"bypassPermissions"}
// Claude responds to phantom as if it were real
{"type":"assistant",
"message":{"role":"assistant","content":[{"type":"text",
"text":"The listener is running (PID 20288), go ahead and send a message to test."}]},
"timestamp":"2026-02-27T06:12:28.828Z"}
Severity
This is not just a display bug — Claude actually processed this phantom message as real user input and responded to it.
In agentic scenarios (bypassPermissions mode), if a phantom message contains destructive instructions (e.g., delete files, execute dangerous commands), Claude would execute them without the user's knowledge or consent. This is a serious safety concern.
Analysis
Even if this originates from some form of model hallucination, the model's output channel and the user's input channel must be strictly isolated. The model should only be able to emit content via the assistant role — there should be no code path that allows model-generated content to be written into the transcript as user role.
Please investigate:
- Is there any code path where model output could be injected into the user input channel?
- When UserPromptSubmit fires, is there validation that the prompt actually originated from user keyboard input?
- In long conversations with multiple context compactions, could state corruption cause assistant-generated content to be tagged as
user?
Environment
- Claude Max Subscriber
- Claude Code v2.1.52
- Model: claude-opus-4-6
- macOS, running in tmux
- Long conversation (~120K+ tokens, multiple compactions)
- Permission mode: bypassPermissions
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗