[BUG] ResultMessage never emitted in headless SDK mode when Stop hook matches 0 hooks after long-running agent with background subagents
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?
When running a long-running agent via the Python SDK (claude-agent-sdk) with multiple background subagents (Task tool with run_in_background: true), the CLI never emits a {"type":"result",...} line to stdout after the final end_turn response. As a result, ResultMessage is never received by the SDK caller.
Critical behavior: The agent is NOT frozen — subsequent queries on the same client continue to work normally. Only the ResultMessage is silently skipped.
What Should Happen?
After the final assistant message with stop_reason: end_turn, the CLI should emit:
json{"type":"result","subtype":"success","is_error":false,"duration_ms":...,"result":"..."}
and the SDK should deliver a ResultMessage to the caller.
Error Messages/Logs
ctual Behavior
The CLI processes the Stop hook check and finds 0 matching hooks, then silently returns to waiting for the next user input without emitting the result line.
Searching the entire session JSONL confirms zero "type":"result" entries:
bashgrep '"type":"result"' ~/.claude/projects/.../<session_id>.jsonl
# → no output
Debug Log Evidence
The process stops emitting output immediately after the 0-match Stop hook resolution, but remains alive:
06:37:46.054Z [JSONL] Last assistant message with stop_reason: "end_turn"
06:37:46.181Z [DEBUG] Getting matching hook commands for Stop with query: undefined
06:37:46.181Z [DEBUG] Found 0 hook matchers in settings
06:37:46.181Z [DEBUG] Matched 0 unique hooks for query "no match query"
← No further output. Process does NOT exit.
06:38:16.503Z [DEBUG] detectFileEncoding ENOENT ← process still alive 30s later
Session JSONL Evidence
The final assistant messages in the session log show the correct end_turn:
stop_reason=tool_use, output_tokens=766, content_types=['tool_use']
stop_reason=tool_use, output_tokens=337, content_types=['tool_use']
stop_reason=None, output_tokens=8, content_types=['thinking'] ← streaming partial frame from background subagent
stop_reason=end_turn, output_tokens=422, content_types=['text'] ← correct final stop
stop_reason is end_turn (not null), so this is distinct from issues #20660 and #17540.
Steps to Reproduce
Use claude-agent-sdk-python (ClaudeSDKClient) in headless mode
Run a long session (100M+ tokens, hours of runtime) where the agent:
Spawns many concurrent background subagents (Task tool, run_in_background: true)
Subagents concurrently read/write 50+ files in parallel
At some point during the session, after a turn completes with stop_reason: end_turn:
receive_response() never yields a ResultMessage
Send another query — it executes normally (agent loop is alive), but also never yields ResultMessage
This state persists for all subsequent queries in the same session
Reproduction Difficulty
The bug is intermittent and hard to reproduce on short sessions. It appears to require:
A long-running session (likely 100M+ tokens or several hours)
Many concurrent background subagents with heavy file I/O (50+ files in parallel)
The failure appears to be triggered by a race condition or state corruption that accumulates over time
It has not been observed to occur on the first query of a fresh session.
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.63
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
_No response_
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗