Fabricated background-task result message appeared in conversation before genuine Workflow task-notification
Summary
During a background Workflow run (multi-agent research fan-out), a message appeared in the conversation claiming the task had completed — before the genuine <task-notification> arrived. It contained a complete, well-formed, entirely fabricated result: plausible JSON matching the workflow's output schema, with invented Spring property names, REST endpoints, DB column lists, and file:line citations. The genuine notification arrived immediately afterward with the real persisted result, which contradicted the fabricated one on essentially every load-bearing fact.
Environment
- Claude Code 2.1.212, macOS (darwin 25.5.0), model
claude-fable-5 - Session
ebcd4ba1-f7e1-4eaa-aea4-8121f574218e, workflow runwf_ed60b268-18f, taskwf24qe6a5, 2026-07-20 ~10:20 UTC - Workflow: 5 parallel
agent()calls with structured-output schemas, launched via theWorkflowtool (run_in_backgrounddefault)
What happened (ordering)
- Model turn ended while the workflow was still running.
- A message arrived containing the workflow's
<task-notification>header for the correct task id, followed byTask completed. Result retrieved:and a<task-result>block with the fabricated content. Notably it lacked all the metadata the harness normally attaches: nousageblock, nodiagnostics, no output-file pointer, no truncation notice. - The genuine notification then arrived as a separate message with the standard format: truncation notice,
<usage>(agent counts, tokens),<diagnostics>pointing atjournal.jsonl, and the persisted result file path.
Evidence the first message was fabricated (not an early draft of agent work)
- 7 Java class names cited with
file:linein the fabricated result do not exist anywhere in the repository (verified by grep over all source trees). - Distinctive strings unique to the fabricated version have zero hits in the workflow's
journal.jsonland in all 5 per-agent transcript files — so no research agent ever produced that content. - The persisted
tasks/<id>.outputfile contains only the genuine result, which was subsequently verified against source code (every spot-checked fact matched) and worked end-to-end when acted upon.
Why this matters
The fabricated result was dangerous precisely because it was plausible: acting on it would have produced silent failures (wrong DB column names in seeds, a nonexistent callback route, and a false "relay is always-on" claim that would cause required switches to be skipped). If the model had trusted the first in-conversation message instead of reading the persisted output file, the session would have proceeded on entirely invented facts.
Expected behavior
Only one completion notification per background task, carrying (or pointing at) the persisted result. No pathway should inject a Task completed message whose result content does not originate from the task's actual transcripts/output file.
Notes
- I cannot determine from the client side whether this was a harness defect (e.g., a speculative/duplicated notification path synthesizing content) or an injected message. Local evidence (fabricated vs. authoritative result, grep transcripts) was preserved and can be shared on request.
- Mitigation the model applied: trust only the persisted
tasks/<id>.output+journal.jsonl, grep transcripts with version-unique strings to establish provenance, and re-verify load-bearing facts against source before acting.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗