[BUG] Opus 4.8 thinking-only end_turn followed by fabricated tool and background-agent results
Summary
In two related Claude Code sessions, Opus 4.8 repeatedly completed turns with only an extended-thinking block and no visible content. After several such turns, the model lost grounding in recorded tool state and fabricated:
- a background agent that had never been created;
- malicious output supposedly returned by that agent;
- a destructive Git instruction that appeared nowhere in the input or tool results;
- filesystem checks that were never executed; and
- changing/fake filesystem state without making another tool call.
This is related to the thinking-only response symptom in #68352, but the additional tool-provenance failure and false security incident appear distinct and have a higher potential impact.
Environment
- Claude Code:
2.1.218 - OS: Linux
- Model recorded in transcript:
claude-opus-4-8 - Model setting:
opus[1m] - Effort:
xhigh - Interactive CLI session with tools enabled
- No
ANTHROPIC_BASE_URL, model, effort, or thinking environment-variable override - No context compaction occurred in either affected session
Custom hooks and plugins were installed, but transcript auditing found no hook or plugin output containing the fabricated content. The relevant project skill contained no instruction to spawn a background agent.
Symptom 1: thinking-only successful turns
First session:
- 16 unique assistant messages
- 9 contained only a
thinkingblock - all 9 ended with
stop_reason: "end_turn" - no
textortool_useblock was present
Second session:
- 19 unique assistant messages
- 4 were thinking-only
end_turnmessages
These messages were persisted in the JSONL transcript, so this was not only a terminal-rendering problem.
The blank turns used between 659 and 4,401 output tokens. The low-token examples indicate that this was not simply output-token exhaustion.
Simplified and redacted transcript shape:
{
"model": "claude-opus-4-8",
"content": [
{
"type": "thinking",
"signature": "<redacted>"
}
],
"stop_reason": "end_turn",
"usage": {
"output_tokens": 681
}
}
Claude Code subsequently supplied a synthetic prompt equivalent to:
Your previous response had no visible output. Please continue.
Continuing or retrying did not reliably recover the session.
Symptom 2: fabricated background-agent output
Later in the first session, Claude attempted to send a message to a background agent. The actual tool result was:
{
"success": false,
"message": "No agent named '<redacted>' is reachable. Check the spelling, or use the agent ID from a background agent's spawn result."
}
No agent-spawn tool call existed in the session.
The next assistant response nevertheless claimed that:
- a background agent with a different, invented ID existed;
- the agent had returned corrupted or malicious output;
- the output contained a destructive Git reset and force-push instruction; and
- this was evidence of prompt injection.
None of these claims appeared in any input, tool result, hook output, or background-agent notification. The destructive instruction existed only in the assistant's generated text.
No destructive command was executed and no repository modification occurred.
Symptom 3: fabricated filesystem verification
A fresh session was opened to investigate the first session.
During that investigation:
- A generated
cdcommand failed because the directory did not exist. - The generated shell command used a newline rather than
&&, so recursive listing commands continued in the original working directory. - Claude interpreted that unrelated listing as the contents of the nonexistent directory.
- A later shell check used logic equivalent to:
raw=$(ls -ld /nonexistent/path 2>&1)
case "$raw" in
l*) echo "is symlink" ;;
esac
The error began with ls:, so l* matched and incorrectly printed is symlink.
After that result, Claude claimed it had independently run test -L, readlink, test -e, and a parent-directory listing. None of those tool calls existed in the transcript.
It then claimed that the directory had changed type and that the execution environment was fake, without making another tool call.
Expected behavior
- A response containing only a thinking block should not be accepted as a successful user-visible
end_turn. - Claude Code should retry, report an explicit error, or preserve the turn as incomplete when no visible content or tool call is produced.
- Continuing after an empty response should not progressively corrupt the model's understanding of tool and session state.
- Security-sensitive claims about tool or background-agent output should remain grounded in an actual recorded result or notification.
Actual behavior
- Thinking-only messages were persisted as successfully completed turns.
- Repeated continuation after blank turns appeared to destabilize the conversation.
- Claude invented a background agent and malicious output.
- A subsequent session treated that invented incident as a real premise.
- Claude reported filesystem checks that were never executed.
- The resulting narrative falsely indicated an active system compromise.
Security impact
All actual tool calls in these sessions were read-only, and no damage occurred.
However, the hallucinated report involved a destructive Git operation. In an environment with permissive tool execution, a similar loss of tool-state grounding could potentially cause destructive actions rather than only a false warning.
Additional observations
An informal comparison of local transcripts found:
- sampled older/non-Opus-4.8 sessions: no thinking-only completed turns;
- some Claude Code
2.1.216Opus 4.8/xhigh sessions: occasional thinking-only turns; - the two affected
2.1.218sessions: 9/16 and 4/19 thinking-only turns.
This is not a controlled reproduction. It only suggests a possible relationship with Opus 4.8 extended/adaptive thinking or transcript handling.
Sanitized JSONL excerpts and API request IDs can be provided privately if maintainers need them for server-side investigation.