Skip to content

ClaudeSDKClient hangs on Windows during initialization #208

@andreboom

Description

@andreboom

Description

ClaudeSDKClient hangs indefinitely during initialization on Windows when entering the async context manager. The SDK successfully spawns the Claude CLI subprocess but never receives the expected control_response to the initialization request, causing it to timeout after 60 seconds.

Environment

  • OS: Windows 11
  • Python Version: 3.13
  • claude-agent-sdk Version: Latest
  • Claude CLI Version: 2.0.8
  • Shell: PowerShell / Git Bash

Steps to Reproduce

import asyncio
from claude_agent_sdk import ClaudeSDKClient, ClaudeAgentOptions

async def main():
    options = ClaudeAgentOptions(
        model="sonnet",
        permission_mode="acceptEdits",
    )
    
    print("Initializing...")
    async with ClaudeSDKClient(options=options) as client:
        print("Client ready!")  # Never reaches here
        await client.query("Hello")
        async for msg in client.receive_response():
            print(msg)

asyncio.run(main())

Expected Behavior

The client should initialize successfully and print "Client ready!" within a few seconds.

Actual Behavior

The script hangs at the async with ClaudeSDKClient(options=options) line for 60 seconds, then fails with:

Fatal error in message reader: Command failed with exit code 143 (exit code: 143)
Error output: Check stderr output for details

Root Cause Analysis

Investigation shows:

  1. The SDK successfully spawns the Claude CLI subprocess with arguments like:

    claude --output-format stream-json --verbose --model sonnet --input-format stream-json --permission-mode acceptEdits --setting-sources ""
  2. The SDK sends an initialization control request via stdin:

    {"type":"control_request","request_id":"req_1_...","request":{"subtype":"initialize"}}
  3. The CLI subprocess never responds with a control_response, causing Query.initialize() to hang waiting for the response (line 309-310 in _internal/query.py).

  4. Testing the exact same command manually in Git Bash/PowerShell works correctly:

    echo '{"type":"control_request","request_id":"test123","request":{"subtype":"initialize"}}' | claude --output-format stream-json --verbose --model sonnet --input-format stream-json --permission-mode acceptEdits --setting-sources ""

    This immediately returns the expected control_response.

  5. The issue appears to be Windows-specific subprocess stdin/stdout buffering or communication issues when spawning the process via Python's anyio.open_process().

Workaround

The issue does not occur when running the same code in WSL (Windows Subsystem for Linux).

Additional Context

  • The query() function works fine on Windows (non-interactive mode)
  • Only ClaudeSDKClient (interactive/streaming mode) is affected
  • The subprocess is successfully spawned but the bidirectional communication fails
  • Stderr capture shows no output from the CLI subprocess

Suggested Fix

This may require Windows-specific handling of subprocess pipes, possibly:

  • Setting appropriate buffer modes for stdin/stdout
  • Using different process creation flags on Windows
  • Ensuring stdin is properly flushed after writing
  • Investigating if anyio.open_process() has known Windows issues with stdin/stdout pipes

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions