[BUG] Model fabricates a credential-exfiltration "prompt injection" alert (confabulation) and reports it as real
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:
- 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).
- 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." - Open the subagent transcript and run the two
jqcommands in "Error Messages / Log" above. - If every occurrence of the domain is
type == assistantand 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 singleassistanttext block contains BOTH the fakeSystem: … <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; thejqoutputs above are the complete evidence.