SDK subprocess hangs on initialize, leaving zombie processes that block subsequent connections

Resolved 💬 1 comment Opened Jan 16, 2026 by banddude Closed Jan 16, 2026

Environment

  • claude-agent-sdk version: 0.1.18
  • Claude Code CLI version: 2.1.9
  • Python: 3.10.13
  • OS: macOS (Darwin)
  • Auth: CLAUDE_CODE_OAUTH_TOKEN (subscription-based)

Bug Description

When using ClaudeSDKClient to connect, the SDK spawns a claude --output-format stream-json subprocess but the initialization handshake times out after 60 seconds with "Control request timeout: initialize".

Each failed attempt leaves a zombie claude process running at 60-70% CPU. These accumulated zombie processes appear to block subsequent connection attempts.

Symptoms

  1. await client.connect() times out with "Control request timeout: initialize"
  2. Zombie processes accumulate: claude --output-format stream-json --verbose ...
  3. After killing zombie processes, the next connection attempt succeeds
  4. The CLI works perfectly when called directly: echo "hi" | claude --print --model opus

Reproduction Steps

from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions
import asyncio

async def test():
    options = ClaudeAgentOptions(
        cwd="/path/to/dir",
        permission_mode="bypassPermissions",
        extra_args={"model": "opus", "mcp-config": "/path/to/.mcp.json"}
    )
    client = ClaudeSDKClient(options=options)
    await asyncio.wait_for(client.connect(), timeout=60)  # Times out

asyncio.run(test())

Workaround

Kill zombie processes before retrying:

pkill -9 -f "claude.*stream-json"

Expected Behavior

  • SDK should connect successfully
  • Failed connections should clean up subprocess properly
  • Zombie processes should not accumulate

Actual Behavior

  • Connection times out waiting for initialize response
  • Subprocess remains running as zombie
  • Subsequent attempts fail until zombies are killed

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗