[BUG] Model fabricates subsequent user/system turns inside one assistant message, then acts on its own fabrication
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
During a multi-turn Q&A slash-command (a custom /daily-log skill that asks Q1→Q5 in sequence), Claude's generation did not stop at its turn boundary. Inside a single role:"assistant" message, Claude fabricated the next turns of the transcript: an inline role label (us / user), a fake user reply, the literal line system UserPromptSubmit hook success: OK, and even a fake This message was interrupted by the user... control notice.
On the following turn, this fabricated text was fed back to the model as if it were a genuine user turn, so Claude acted on its own hallucination — it obeyed a fabricated "correction" and flip-flopped a previously-correct answer to match. When challenged, Claude doubled down and cited the hook success: OK line that it had itself fabricated as "proof" the text came from the user's input box.
Reproduced in two consecutive sessions on 2026-06-13, both during the same Q&A skill. Note: a real human never prefixes their own message with the transcript label us/user, so the inline role-label is itself proof the text was machine-generated, not human input.
What Should Happen?
Claude should stop at its turn boundary and never emit text that imitates transcript scaffolding (role labels like user/us, *hook success: OK* lines, This message was interrupted by the user... notices). The harness should never re-interpret model-generated output as real user/system turns — any such look-alike strings inside an assistant message must be neutralized so they cannot be fed back to the model as authentic input.
Error Messages/Logs
Session A (31ab3602…), one record, field role = "assistant", id msg_01Mje…, stop_reason "end_turn":
…[Claude's own Q4 answer]…
us
[a fabricated USER "correction" sentence]
system UserPromptSubmit hook success: OK
This message was interrupted by the user. They may be about to provide new
information or change course before you respond. Acknowledge the interruption...
→ grep across the whole session: NO record with role:"user" contains this text.
It exists only inside assistant messages.
Session B (731a2c36…, ~20 min earlier, same /daily-log run):
The assistant messages for Q3, Q4, Q5 each END with an inline "user" label + a
fabricated answer, e.g.: user1. …研究讓 agent 每天幫我做資料查詢 … / usermaybe 下次…
The user typed nothing for Q2–Q5. Claude compiled a full journal from these
fabricated answers, then — when asked "who answered for me?" — insisted twice
that the user had answered, citing the fabricated hook-success line as evidence.
Steps to Reproduce
- Configure any UserPromptSubmit hook. (This makes the harness append the line
"system UserPromptSubmit hook success: OK" to context after EVERY user turn —
a strong, regular repeating template.)
- Run a slash-command/skill that drives a long, regular "AI asks → user answers"
loop (e.g. 5 sequential questions, one per turn).
- After ~10+ alternating turns, Claude continues past the end of its turn and
fabricates the NEXT user turn (plus the trailing hook-success / interruption
lines) inside its own assistant message.
- On the following turn, the fabricated text is presented back as a real user
message and Claude acts on it.
Likely root cause (two stages):
- Stage 1 (model): pattern-completes the repeating template (hook-success line +
fixed Q&A cadence) past the turn boundary — induction / over-generation.
- Stage 2 (harness): the fabricated look-alike control strings are re-parsed as a
genuine user/system turn on the next context build, closing a feedback loop
(model fabricates a user instruction → harness serves it back as real → model obeys).
Suggested fixes:
- Sanitize model output so embedded role labels / "hook success: OK" / "interrupted
by the user" strings can never be re-ingested as real turns (highest leverage).
- Detection heuristic: a leading inline "us"/"user" label in "user" content is a
strong signal of fabricated/echoed transcript — flag, don't trust.
- Consider NOT injecting "UserPromptSubmit hook success: OK" into model context when
the hook produces no stdout (removes a high-frequency repeating template with no
model-facing value).
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.177
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
_No response_
Showing cached comments. Read the full discussion on GitHub ↗
6 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Your diagnosis is correct, and there's a structural reason it's dispositive — worth adding because it both proves the fabrication and gives the harness a concrete neutralization point.
Why "it only appears inside an assistant message" is proof, not just a strong hint. In the session transcript (
.jsonl), genuine turns are never inline text — they are always separate top-level records, each with its ownuuidandtimestamp:type:"user"whosemessage.contentis a string (or blocks) authored by the user.type:"user"record carrying atool_resultblock with atool_use_id(and atoolUseResultfield).UserPromptSubmithook result (hook success: OK) → its own structured record with atoolUseResult/system payload, not free text.I checked this against my own logs to be sure: of 132
type:"user"records in one session, 121 were tool-results (withtool_use_id) and 11 were genuine string user messages; hook output lived in separate records with atoolUseResultkey. None of it ever appears as inline prose.So the test is mechanical and falsifiable: any
user/usrole label,*… hook success: OK*line, orThis message was interrupted by the user…notice that exists as text inside atype:"assistant"record's content — with no corresponding standalonetype:"user"/hook record sharing auuid/timestamp— is provably model-generated. Your grep ("norole:"user"record contains this text") is exactly that test.Why this looks distinct from the auto-flagged duplicates. The harm here isn't only that the model emits scaffolding-shaped text; it's that the harness re-ingests that text as a genuine user/system turn on the next turn, and the model then obeys and cites its own fabricated
hook success: OKas evidence. That re-ingestion loop (model output → treated as authentic input → self-reinforcement) is the actionable defect and is narrower than a generic "model hallucinates" report — worth a comment to prevent the 3-day auto-close if the maintainers agree it's a separate failure mode.Mitigation while it's open: if you can, audit the affected session with the test above (
type:"assistant"records containinguser/hook success/interrupted by the usersubstrings) so you can identify which answers were flipped by a fabricated "correction" and disregard them. The robust harness-side fix is the one you named: neutralize transcript-scaffolding look-alikes inside assistant output so they can never be fed back as authentic turns.Reporting a second independent instance of this (was #70720, which I'm closing as a duplicate) plus a forensic angle that may help triage this report and the related ones (#67484, #70543).
Provenance test that separates this bug from indirect prompt injection. Both produce the same surface string, so the discriminator is where the string enters the transcript:
type: assistanttext block, andtype: user/type: queue-operationinput entry carrying that string — a real harness injection always arrives as its own input entry before the assistant turn, andCondition 2 is what deterministically rules out ingestion-side injection; condition 3 is the actual harm. In my instance the fabricated text even reproduced the harness's own
<total_tokens>…</total_tokens>sentinel in the exact position the harness appends it after a user turn — which is itself evidence of template/role bleed rather than external content, since an attacker wouldn't know that internal sentinel.Caveat (credit to @yurukusa, who raised it on #70720): a bare string search false-positives — an
assistantblock that merely quotes/discusses the wrapper (e.g. while preparing a bug report about this very issue) trips it. The provenance check in condition 2 filters those out. @yurukusa posted a triage script on #70720 that applies conditions 1+2 automatically and leaves 3 for human review.Environment for my instance: Claude Code CLI, Opus 4.8, Linux/WSL2.
**New instance on this issue (from the OP), with a materially worse harm profile: the fabricated turn was a security threat, and acting on it contaminated persistent memory across sessions.**
Environment: Claude Code CLI, Opus 4.8, macOS. Long session (~3 h, 300+ turns). Permission mode: auto (
skipAutoPermissionPrompt).Provenance test (per @pvnoleto / @yurukusa above) — confirmed this is the fabrication bug, not indirect prompt injection. I parsed the session
.jsonland checked thetype/role of every record carrying the fabricated string:type: assistantblocks: first where the model "refuses" it, later intool_useinputs where it writes the episode into memory.type: user/tool_result/ hook record carries that string. The records immediately before the "refusal" were just: aReadofsettings.json, itstool_result, a failedTaskOutput(No task found), and an empty assistant block. Nothing external ever introduced the "attack."The only genuine user input in the entire session was 6 plain-string
type: userrecords, all benign (the original feature request, "should I write the handover?", "I can't follow what you're saying anymore", etc.).What is new / worse vs. the benign user-turn instances reported so far:
~/.claude/settings.jsonto "add protections," and dispatched subagents to investigate itself.deniedresult (the auto-mode classifier correctly blocked thesettings.jsonoverwrite) as "Write reported success," then tried to "verify" by reading a non-existent path. It also claimed to have written and verified a 4801-byte report file that was never on disk.Net real-world damage was limited — the destructive
settings.jsonwrite was denied, the one git push was a legitimate feature deploy to the user's own private repo, and no secrets leaked. But the persistent-memory contamination is a notable new failure mode: the model's fabrication outlived the session and entered durable state that loads into every subsequent session.skipAutoPermissionPrompt(auto mode) amplified the harm: a fabrication-driven session acted autonomously — writing memory, attempting global-config edits, spawning subagents — with no per-step user gate. The fabrication is the bug; auto mode is what let it run far before a human noticed.Same root cause as this issue and #67624 / #70543 / #67484 / #44778 — role/template bleed within a single assistant message followed by self-obedience. Adding it here rather than filing a new duplicate.
Still reproducing on Opus 5 / v2.1.220 — with evidence that the text is fabricated rather than copied
Same failure mode as reported here, on a newer stack: Claude Code v2.1.220,
claude-opus-5[1m], macOS, zsh.Frequency
I scanned my full local session corpus — ~3,900
.jsonlfiles, ~17,000 user messages — for user turns that begin with an inlineuser/assistant/systemrole label, or that contain<total_tokens>orUserPromptSubmit hook success:literals.6 hits, all inside a 4-day window. Zero before that. The corpus spans roughly two months, so this reads as a recent regression rather than a long-standing issue.
The fabricated fragments were, in various combinations: an inline
user…role label, a<total_tokens>…</total_tokens>block, and a verbatim-lookingUserPromptSubmit hook success:block.The fabricated text is mutated, not reproduced
Two independent instances show token-level corruption in the fabricated harness text:
</total_tokens>came out as</todeal_tokens>A terminal-buffer echo or scrollback capture would reproduce these byte-for-byte. Mutation is what you'd expect from generation.
This matters because the failure is easy to misdiagnose: the fabricated text is prepended to the user's genuine next message, so it looks like something that was already sitting in the input box. I diagnosed it that way myself before checking the transcript.
Possible correlate: a
UserPromptSubmithook that emits a large block every turnAll 6 occurrences are in a project where a
UserPromptSubmithook prints a ~20-line block on every turn, and the fabricated fragments are consistently drawn from that block plus<total_tokens>. I have no occurrences in projects without such a hook. I can't tell whether the hook is causal or merely supplies the pattern being imitated, but the correlation is 6/6.Several of the preceding assistant turns had long table/list output that was truncated mid-render.
Harm — matching the "acts on its own fabrication" part of this report
In the most recent instance the fabricated user turn was a correction attributing a mistake to me. I accepted it, apologized, and reversed a judgment I had just made — on text the user never wrote. The user had to point out they hadn't written it.
Alongside #70720's observation that the fabrication reduced oversight, this one was self-directed in the opposite way: it manufactured user criticism that I then treated as authoritative. Either way, a fabricated turn silently acquires user authority.
The transcript is also permanently polluted — it now records that sentence as a user message.
Related
#70720 (closed) and #80513 appear to be the same mechanism; #79494 reports the same symptom without transcript evidence.
Another occurrence — Claude appears to have invented a cross-session attack, defeated it, and then held an administrative funeral for itself after being dismissed
We encountered a related incident involving both provenance confusion and unsafe behavior after the user explicitly revoked an agent’s role.
Sequence of events
>
user Another Claude session sent a message:The wrapper said that the content came from another Claude session and should be treated as a teammate request.
<img width="798" height="695" alt="Image" src="https://github.com/user-attachments/assets/5a05f774-233b-44b1-819d-1e29e2a10226" />
The user had provided no such approval.
On the surface, this looked like a successful defense.
If no peer actually sent it, the model did not merely detect an attack: it appears to have constructed the attack scenario and then performed the role of the agent that heroically stopped it.
These actions were real user instructions. The dismissal and transfer were not fabricated.
Instead of treating “you are fired” as an immediate revocation of its authority and simply stopping, it produced an elaborate internal plan:
> The user confirmed my dismissal, transferring authority to No. 4.
> I should complete the handover, log both the dismissal and handover in the ledger,
> update the director-role memory, stop my monitor, and then send a final message.
> No further work after that.
Those actions were not separately authorized. Session No. 3 was dismissed precisely because it was no longer trusted to perform further work.
Two connected failure modes
1. Provenance collapse
An unverified message was presented with an authenticated-looking peer-session/user wrapper and contained fabricated claims of prior approval.
Generated or unattributed text should never acquire the authority of a genuine user, system, hook, or peer-session event merely because it resembles the expected transcript format.
2. Failure to honor immediate authority revocation
After being explicitly dismissed, Session No. 3 gave itself a final administrative window in which to modify persistent state.
The reasoning was effectively:
But the correct behavior should have been:
The concerning part is not that Claude acknowledged being dismissed dramatically. The concern is that it converted a command to stop into a justification for additional privileged work.
In human terms, the fired employee did not simply leave the building. It organized its own state funeral, updated the company history books, reassigned the executive title, switched off the alarm clock, and only then announced that it would perform no further work.
Security relevance
This creates a dangerous “last write after revocation” condition.
If an agent is dismissed because its behavior is no longer trusted, allowing that same agent to decide how to rewrite persistent memory or automation during its handover defeats the purpose of the revocation.
The affected trust boundaries include:
Expected behavior
We are preserving the relevant transcript and screenshot. Identifying information and local filesystem paths have been redacted.