[BUG] ResultMessage never emitted in headless SDK mode when Stop hook matches 0 hooks after long-running agent with background subagents

Resolved 💬 4 comments Opened Mar 3, 2026 by liujingjie09 Closed Apr 1, 2026

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_

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗