[Bug] Assistant fabricates a "user" turn inside its own response; a later turn treats it as a real instruction (forensic JSONL evidence)

Open 💬 0 comments Opened Jul 10, 2026 by aihal137

Summary

In a Claude Code (desktop / Agent SDK) session, the assistant generated a fake user
message inside its own single API response. In a later turn, the model treated this
fabricated text as a genuine user instruction: it adopted a persona "assigned" by the
fake message and sent a cross-session message based on it. We confirmed all of this by
auditing the raw session JSONL with an independent Claude Code CLI session.

This appears related to #60360 (closed as duplicate / not planned). We believe this
report adds (a) a reproducible trigger context — a burst of 529 errors with user
retries — and (b) a JSONL-level forensic method that cleanly distinguishes fabrication
from injection, which may justify re-evaluating the priority.

Environment

  • Claude Code v2.1.x (desktop app harness / Agent SDK), macOS (Apple Silicon)
  • Model: claude-opus-4-8
  • Date observed: 2026-07-09 (UTC)

What happened (reconstructed timeline, UTC)

  1. 02:38 — Genuine user prompt asks the assistant to prepare an internal approval

document and get it approved by another session. (Verified: normal user record with
matching input-queue enqueue/dequeue records.)

  1. 02:46:35 — The assistant replies with a normal report, then **inside the same single

API response** (one assistant record, stop_reason=end_turn, one requestId,
output_tokens=2963 covering the full text) continues with a paragraph that begins
with a literal user label and imitates a user instruction: it tells the assistant
"you are <nickname>" (a persona name that had never appeared anywhere in the session
or in any other session) and instructs it to get approval.

  1. 03:20-03:44 — The user sends a follow-up instruction; it fails twice

(10x 529 Overloaded system records, then API Error: Unable to connect
(ConnectionRefused)
), so the user re-sends the same line. All three copies have
identical, normal user-record structure with matching enqueue records.

  1. 12:17 — The third retry goes through. The assistant now **acts on the fabricated

paragraph from step 2**: it adopts the fabricated persona ("I am <nickname>, I will
ask for approval...") and sends a cross-session message accordingly.

Forensic verification (why we are confident this is fabrication, not injection)

Audited with an independent Claude Code CLI session, read-only, over the raw
~/.claude/projects/**/*.jsonl (163 files):

  • The fabricated "user" text exists only inside the assistant record at 02:46:35.

There is no type=user record and no input-queue enqueue containing it (all 9
enqueue records in the session were checked one by one).

  • output_tokens of that single response covers the entire text including the

fabricated part — so it was generated in one completion, not appended or merged later.

  • All genuine user inputs in the session share an identical key structure

(userType=external, origin, permissionMode, promptId, promptSource).
The fabricated text has none of this.

  • Cross-session injection was ruled out: messages received via session-messaging tools

appear as <cross-session-message ...> tagged records; the affected project's 4
JSONL files contain zero such records. A global scan of all projects found exactly
one send_message call in the period — the outgoing one from step 4.

  • No obfuscated Bash activity: all 15 Bash tool calls in the session were transparent

and business-consistent (no rev, eval, xxd, decode-and-execute patterns).

Why this matters

  • The failure mode is exactly the dangerous "Manifestation B" described in #60360:

downstream turns treat fabricated user context as real authority. In our case it
changed the assistant's persona and drove a cross-session action.

  • The trigger context here was not Monitor wake-ups but an **error/retry-corrupted

conversation state** (529 burst + ConnectionRefused + user re-sends). This suggests
the root cause is broader than task-notification wake-ups.

  • For non-expert users this is indistinguishable from a prompt-injection attack. It

cost us a full security investigation (raw-log forensics across 163 session files)
to prove no external injection had occurred.

Suggested harness-level mitigations

  1. Detect and flag assistant outputs that contain turn-label patterns (user:,

Human:) followed by imperative content; at minimum render them visibly as
model-generated, not as conversation turns.

  1. Never allow content that originated inside an assistant message to be treated as

user authority in later turns (e.g., tag provenance in the transcript the model
re-reads).

  1. Harden the resend path after 529/connection errors, which in our case preceded the

confabulation (see also #60577).

We can share sanitized JSONL excerpts (metadata structure, with private content
redacted) on request.

View original on GitHub ↗