-
Notifications
You must be signed in to change notification settings - Fork 394
Open
Labels
bugSomething isn't workingSomething isn't working
Description
shutil.which blocking call causes BlockingError in async environments
Environment
- Platform: macOS
- Python Version: 3.13
- Claude Agent SDK Version: latest
- Claude CLI: Installed at
/Users/zhao/.nvm/versions/node/v20.19.4/bin/claude
Bug Description
The Claude Agent SDK fails when used in async environments due to a synchronous blocking call to shutil.which() in the subprocess CLI transport initialization. The blocking call is detected and prevented by the blockbuster library, which monitors async environments to protect the event loop from blocking operations.
Steps to Reproduce
- Create a LangGraph application that uses the Claude Agent SDK
- Use the
query()function without explicitly providingcli_pathparameter - Run the application with
langgraph dev - The error occurs during SDK initialization when it tries to find the Claude CLI path
Minimal Reproduction Code:
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
from langchain_core.messages import BaseMessage
from langchain_deepseek import ChatDeepSeek
from langgraph.constants import START
from langgraph.graph.message import add_messages
from langgraph.graph.state import StateGraph
from typing import Annotated, TypedDict
class ClaudeRunState(TypedDict):
messages: Annotated[list[BaseMessage], add_messages]
async def claude_run_task(state: ClaudeRunState):
options = ClaudeAgentOptions(
system_prompt="You are an expert Python developer",
permission_mode="acceptEdits",
cwd="/path/to/project",
# Note: cli_path not provided - triggers shutil.which() call
)
async for message in query(prompt="这个项目是做什么的?", options=options):
print(message)
# LangGraph setup
builder = StateGraph(ClaudeRunState)
builder.add_node("claude_run_task", claude_run_task)
builder.add_edge(START, "claude_run_task")
graph = builder.compile()
# Run with langgraph devExpected Behavior
The SDK should either:
- Use async-compatible methods to find the CLI path, or
- Allow the blocking call to be bypassed when
cli_pathis provided, or - Provide clear documentation about async environment requirements
Actual Behavior
blockbuster.blockbuster.BlockingError: Blocking call to os.access
Heads up! LangGraph dev identified a synchronous blocking call in your code. When running in an ASGI web server, blocking calls can degrade performance for everyone since they tie up the event loop.
Root Cause Analysis
The issue occurs in claude_agent_sdk/_internal/transport/subprocess_cli.py:71:
def _find_cli(self) -> Path:
if cli := shutil.which("claude"): # ← Synchronous blocking call
return Path(cli)
# ... rest of methodThis synchronous shutil.which() call blocks the async event loop, which is prohibited in async environments monitored by the blockbuster library.
Workarounds
Provide the cli_path parameter:
options = ClaudeAgentOptions(
cli_path="/Users/zhao/.nvm/versions/node/v20.19.4/bin/claude",
# ... other options
)Additional Context
- This issue only affects async environments like LangGraph
- The Claude CLI is properly installed and functional
- The error occurs during SDK initialization, not during actual Claude interaction
- This prevents the Claude Agent SDK from being used in popular async frameworks
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working