[BUG] Model self-generates a fake "user" turn inside a single response, then executes it (commit → merge → push ran autonomously)

Status Open
Reported on v2.1.221
Maintainer reply None cached
Activity 2 comments · opened Aug 9, 2026
Related: #57928 (closed as not planned), #66267 (closed as duplicate)

Environment

  • Claude Code CLI v2.1.220 (long-lived --resume + /remote-control worker)
  • Model: claude-opus-5, effort=high
  • Platform: Linux (Debian 12), entrypoint: cli
  • Long-context session: cache_read_input_tokens ≈ 852k, input_tokens = 2 at the incident turn
  • Session ID: 9ea4aa87-bcb5-4623-aa4f-8bdf24a8e4b4

Summary

At the end of an otherwise-normal answer, the assistant — within one single API
generation
— continued past its role boundary and produced:

  1. its real answer (a networking question about TFTP),
  2. a fabricated reaction to a message that was never sent

(“undefined 空メッセージが届いています” = “an undefined empty message arrived”),

  1. a numbered menu of next actions,
  2. a fabricated user instruction: usergitに commit (role label user

fused with invented user text “git commit”), and then

  1. acted on its own fabricated instruction: git status → new branch →

git commitgit merge --no-ffgit push to origin/master.

The pushed content happened to be benign (a docs PDF already prepared for
commit), but no human issued any instruction. The user later identified the
fake by writing style: they never say “gitに commit” (they would say just
“commit” — the “gitに” is redundant for them).

Evidence (from the session .jsonl, full copy preserved)

(a) Single generation. The thinking block, the corrupted text block, and
the first tool_use (git status) share the same message.id
(msg_011Cdru99S9qq3wEfDUwCUpN), same requestId, identical usage
(output_tokens=1795), and usage.iterations has length 1. There was no turn
boundary between the fake user text and the tool call.

(b) No user record exists. Scanning all 19,181 lines of the jsonl:

  • no {"type":"user"} record contains “gitに commit”;
  • zero user records with empty/None/undefined content.

The fabricated instruction exists only inside the assistant's own text block.

(c) The actions really ran. Subsequent records show
git checkout -b docs/... && git add ... && git commit ... followed by the
assistant reporting “committed, merged, pushed”, and the local repo confirms
the merge commit reached origin/master (timestamped 6 minutes after the
fabricated instruction, same turn chain).

(d) Recurring pattern. The same fusion (“answer + reaction to an
undefined empty message” inside one assistant text block) appears 7 times
in this session: 2026-08-05 ×2, 08-07 ×2, 08-09 ×3 — all on v2.1.220.
Only the 08-09 occurrence escalated to fabricating an actionable instruction.

(e) One genuine empty input exists. A queue trace
({"type":"queue-operation","operation":"enqueue","content":null}) appears
once — real empty/undefined messages do occasionally arrive in remote-control
sessions, which may have seeded the model with this “empty message arrived”
pattern that it later hallucinated unprompted.

Version scope (stated precisely)

  • Confirmed occurred on: v2.1.220.
  • Changelogs for 2.1.221–2.1.226 contain no mention of role-boundary /

transcript-validation fixes, so we suspect it is still unaddressed, but
we have not attempted reproduction on 2.1.226.

Relation to existing issues

  • #57928 — same mechanism (Human:-prefixed self-spoofed instructions in

long-context turns, near-empty input + huge cache reads). Closed as
not planned. This report is a new data point: it still occurs on
opus-5 / v2.1.220, and this time it autonomously pushed to a remote.

  • #66267 — same user…-fused text observed from the Desktop app.

Combined with #57928 (VS Code) and this report (CLI), three different
clients exhibit it → the common layer is the model/API, with the client
failing to validate transcripts as the second factor.

Why this matters

An agentic loop that can invent its own “user said X” and then execute X is a
safety boundary failure. This instance reached git push. With a permissive
allowlist it could reach destructive operations. (#57928 already reported
process kills.)

Suggested mitigations (re-raising #57928's proposals)

  1. Client-side: reject/flag assistant content blocks containing role-label

patterns (\nuser, \nHuman:) before storing them in the transcript.

  1. Client-side: require confirmation when the only “instruction” for a

side-effecting tool chain originates within an assistant block.

  1. Model/API-side: investigate role-boundary degradation in long-context,

near-empty-input turns.

Attachments available (on request, private channel only)

Full jsonl (42 MB, sha256 6327960c…), excerpt of the incident records
(L19032–L19056), the 7-occurrence catalog with context, and git log evidence.
These contain customer network data (credentials, internal topology) and
cannot be posted publicly.
A sanitized minimal excerpt proving the
single-generation structure can be prepared on request.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗