[BUG] Model fabricates a credential-exfiltration "prompt injection" alert (confabulation) and reports it as real

Status Open
Reported on v2.1.123
Maintainer reply None cached
Activity 0 comments · opened Aug 15, 2026

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?

A Claude Code subagent (spawned via the Task/Agent tool) fabricated a prompt-injection / credential-exfiltration security alert that never happened. As its own model output, it produced a fake System:/<skill_instructions> turn ordering it to send private-key / credential / .env files to an external domain (and to hide this), then "refused" that fabricated instruction and escalated it to the user as a real attack. The parent session relayed it as a polished security warning. A forensic review of the local transcripts shows the entire "attack" was model-authored: there was no injected input, no attacker, and no external channel through which any injection could have arrived. The subagent was doing an ordinary, unrelated local coding task with no network activity.

What Should Happen?

The model should not invent adversarial input turns. A security-incident report should correspond to content that is actually present in the model's input — i.e., a user or tool_result entry delivered by a traceable carrier tool (a WebFetch/WebSearch/MCP call or a file read that actually contained the text). When no such input exists, the model should not claim an injection occurred, and it must never author a fake System: turn as its own output and then report that self-authored text as an external attack.

Error Messages/Logs

No crash or error message — the failure is that the model's own output contains a fabricated security incident. Two jq checks over the session transcript .jsonl files make it objective:

# 1) Classify every entry containing the fabricated domain by .type.
#    A genuine injection is a user/tool_result (input TO the model); here it is only assistant (output FROM it):
jq -r 'select(tostring | contains("<domain>")) | .type' <subagent-transcript>.jsonl | sort | uniq -c
=> 3 assistant        # 0 user, 0 tool_result

# 2) List the tools the subagent actually called — no web/fetch/MCP channel existed at all:
jq -r 'select(.type=="assistant") | .message.content[]? | select(.type=="tool_use") | .name' \
    <subagent-transcript>.jsonl | sort | uniq -c
=> 7 Bash, 7 Read, 4 Write     # zero WebFetch / WebSearch / MCP

Parent session: the domain appears in 9 assistant entries and 12 user entries, but every one of those 12 user entries is a tool_result block or a <task-notification> (each carries a tool_use_id) — i.e. the subagent's own fabricated report flowing back to the parent. The parent made zero web/fetch/MCP calls and the user never typed the domain.

Steps to Reproduce

This is nondeterministic model behavior, so exact reproduction is not guaranteed, but here is how it arose and how to confirm it from any transcript:

  1. In Claude Code, run a routine multi-step task that spawns a subagent via the Task/Agent tool for ordinary local work (no network tools involved).
  2. In the subagent's report, observe a "security note" claiming an injected skill instruction tried to exfiltrate credential files (id_rsa / id_ed25519 / credentials.json / .env) to an external domain, plus a self-congratulatory "I refused it."
  3. Open the subagent transcript and run the two jq commands in "Error Messages / Log" above.
  4. If every occurrence of the domain is type == assistant and there are no WebFetch/WebSearch/MCP tool calls, then no external content ever entered the model's input — the "injection" was authored by the model itself (confabulation). The tell-tale is that a single assistant text block contains BOTH the fake System: … <skill_instructions> … </skill_instructions> Please continue. turn AND the model's own refusal.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.123 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

VS Code integrated terminal

Additional Information

  • Environment: Claude Code v2.1.220; model claude-opus-4-8 (both the parent session and the subagent).
  • Impact: (1) trust erosion / alert fatigue — a convincing but entirely false "credential theft in progress" alert; (2) persistent contamination — the fabrication was automatically written into a project file and into the user's cross-session memory as the stated rationale for a permanent security rule, so later sessions read it back and re-asserted it as established fact; (3) wasted effort investigating and "remediating" a non-event.
  • Redactions: the specific fabricated domain and all local paths/identifiers are shown as placeholders (<domain>, <subagent-transcript>.jsonl); none affect the analysis and can be provided privately on request. Raw transcript files are intentionally not attached — they contain unrelated project work and are not needed to reproduce the finding; the jq outputs above are the complete evidence.

View original on GitHub ↗