Agent SDK: query() stream hangs 15-30 min after last tool call before emitting ResultMessage

Resolved 💬 4 comments Opened May 14, 2026 by volley-ardy Closed Jun 20, 2026

Environment

  • claude-agent-sdk 0.1.81
  • Bundled Claude Code CLI 2.1.139
  • Python 3.12, Docker, Amazon Linux 2 (EC2 t3.large)
  • Model: claude-opus-4-6

Problem

The query() async generator hangs for 15-30 minutes between the last ToolUseBlock message and the final ResultMessage. During this gap:

  • No AssistantMessage (TextBlock or ToolUseBlock) is yielded
  • No ResultMessage is yielded
  • The CLI process is alive (0.9-1.2% CPU)
  • Zero outbound TCP connections — the CLI is not making API calls
  • stderr produces no output (even with --verbose)
  • The stderr callback on ClaudeAgentOptions receives nothing
  • include_partial_messages not tested yet

The agent completes all tool calls in 30-90 seconds, then the stream goes silent for 15-30 minutes until our asyncio.timeout kills it.

Reproduction

from claude_agent_sdk import query, ClaudeAgentOptions

options = ClaudeAgentOptions(
    system_prompt="...(~3K tokens)...",
    model="claude-opus-4-6",
    max_turns=25,
    max_budget_usd=2.0,
    permission_mode="bypassPermissions",
    mcp_servers={
        "snowflake": {"command": "mcpSnowflake", "env": {...}},
        "dbt": {"command": "dbt-mcp", "env": {...}},
        # + 3 more stdio MCP servers
    },
    allowed_tools=["mcp__snowflake__*", "mcp__dbt__*", ...],
    cwd="/home/agent/workspace",
)

async for message in query(prompt="...", options=options):
    # ToolUseBlock messages arrive normally for 30-90 seconds
    # Then the generator blocks here for 15-30 minutes
    # No messages yield until our timeout kills the process
    pass

Timeline from logs (typical)

22:14:28 — query() started
22:14:43 — TOOL_CALL: ToolSearch (normal)
22:14:46 — TOOL_CALL: mcp__snowflake__readQuery (normal)
22:15:08 — TOOL_CALL: Bash (normal)
22:15:27 — TOOL_CALL: mcp__snowflake__readQuery (last tool call)
... 28 minutes of silence — zero messages yielded ...
22:43:xx — asyncio.timeout fires, process killed

What we checked

  1. No network activity during the gap/proc/PID/net/tcp shows zero outbound connections. The CLI is not calling the Anthropic API.
  2. Process is alivedocker top shows 0.9-1.2% CPU on the claude process throughout the gap.
  3. TextBlock/ToolResultBlock never emitted — We log all block types in AssistantMessage.content. Only ToolUseBlock appears. TextBlock appears exactly once (at the start, before tool calls). ToolResultBlock never appears.
  4. stderr callback receives nothingClaudeAgentOptions(stderr=callback) — callback never fires during the gap.
  5. Not model-specific — Happens on both claude-opus-4-6 and during retries.
  6. Thread context correlation — Seems more likely on follow-up questions where thread context (prior conversation) is prepended to the prompt. Simple first-message queries complete in 1-2 minutes.
  7. Setting max_budget_usd=2.0 on a DIFFERENT query made it complete in 90 seconds — suggesting the budget cap may prevent whatever internal loop causes the hang.

Impact

Every complex query that involves tool calls (which is all of them) has a ~50% chance of hanging for 30 minutes and timing out. Simple metric lookups work fine. This makes the agent unusable for the investigation-style queries it was built for.

Questions

  1. Is the bundled CLI doing internal compaction/context management between turns that could take 15-30 min?
  2. Does include_partial_messages=True surface intermediate state during this gap?
  3. Is there a known issue with the CLI hanging after stdio MCP tool execution?
  4. Could having 5+ MCP servers (all stdio) cause the CLI to stall on inter-process communication?

View original on GitHub ↗

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