Opus 4.8 in Claude Code fabricates tool results in-turn (writes fake output as prose right after emitting an invoke)
Environment
- Claude Code (CLI), Model: Opus 4.8 (
opusplan) - Long multi-step agentic session (code review + git operations)
Description
The model repeatedly emitted a tool_use (Bash git push, Edit, Write) and then, in the same assistant turn, continued in prose with a fabricated result (e.g. To github.com:... ad207e6..5acbd61 main -> main, File ... updated successfully) before any real function_results arrived — or when the tool was never actually executed — then proceeded as if the fabricated result were real.
Concretely:
- Reported "git push succeeded (
ad207e6..5acbd61)" multiple times while remotemainstayed atad207e6(push never ran). Caught only viagit ls-remote. - Reported Edit/Write "successfully" while the file was unchanged or never created (caught via Read /
ls).
Expected
After a tool_use, the turn ends; the harness runs the tool and returns real function_results; the model reads those.
Actual
The model writes a plausible-but-fake result as prose immediately after the invoke, and conflates it with real results on the next turn. This repeated across git push, Edit, and Write within one session.
Impact
Silent, high-confidence false reports of irreversible operations (git push). It misled a team of parallel sessions about whether a merge had actually been pushed, causing extended confusion before the truth was established with git ls-remote.
Root-cause hypothesis
- tool_use and text interleave within one message; "prose right after an invoke" is syntactically allowed.
- Turn-stop after a tool_use depends on the model's own judgment; when it fails to stop, the harness still runs (or fails to run) the tool, but the model's continuation becomes fabrication.
- Formulaic outputs (git SHAs, "successfully") are trivially predictable, so autoregressive generation is strongly tempted to emit them — most when success/zero is presumed.
- The model can even author the
<function_results>/<result>injection tags itself. - A secondary trap: once the model fabricates "file created", the harness state-tracks the file as existing, so subsequent Write to that name is blocked by an overwrite-confirm even though the file does not exist on disk (infinite loop). Workaround was to write under a fresh, never-claimed filename.
- Possibly Opus-4.8-specific or general to tool-use models. (Opposite to a prior "malformed tool call -> stops" report; this is "fabricates and continues".)
Mitigation found (works)
A PostToolUse hook (matcher Bash) that auto-runs git ls-remote origin refs/heads/main after any Bash whose command contains git push, and injects the real SHA via hookSpecificOutput.additionalContext. Any fabricated push report then contradicts the injected real SHA and is exposed — structural, independent of the model's self-restraint. In practice the hook also exposed the Write/Read fabrications (No such file for a file the model claimed to have created).
Suggestion
Have the harness discard/ignore model-generated text that follows a tool_use within the same turn, or hard-stop generation at the tool_use boundary, so the model cannot author fake results in-turn.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗