Issue Summary
The claude_agent_sdk query() function works correctly in isolated tests but fails when called from within a FastAPI/uvicorn server. The SDK's subprocess transport only returns the initial SystemMessage and then terminates, never producing the expected AssistantMessage with response text.
Environment
- claude_agent_sdk version: latest (bundled CLI)
- Python: 3.12
- FastAPI: latest
- uvicorn: latest
- anyio: latest
- Platform: macOS
Reproduction Steps
Working Case (Isolated Test)
# This works correctly - returns 4 messages including text response
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions
async def test():
options = ClaudeAgentOptions(
system_prompt="You are a helpful assistant.",
allowed_tools=[],
max_turns=1,
permission_mode="bypassPermissions",
)
count = 0
async for message in query(prompt="What is 2+2?", options=options):
count += 1
print(f"Message {count}: {type(message).__name__}")
print(f"Total: {count} messages")
asyncio.run(test())
# Output: Message 1: SystemMessage, Message 2: AssistantMessage, Message 3: AssistantMessage, Message 4: ResultMessage
Working Case (Minimal FastAPI Server)
from fastapi import FastAPI
from claude_agent_sdk import query, ClaudeAgentOptions
app = FastAPI()
@app.get("/test")
async def test():
options = ClaudeAgentOptions(
system_prompt="You are a helpful assistant.",
allowed_tools=[],
max_turns=1,
permission_mode="bypassPermissions",
)
messages = []
async for message in query(prompt="What is 2+2?", options=options):
messages.append(type(message).__name__)
return {"messages": messages}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8001)
# Works: Returns ["SystemMessage", "AssistantMessage", "AssistantMessage", "ResultMessage"]
Failing Case (Reportify Codex Server)
# Same query() call fails when invoked from within the Reportify codebase
# Returns only: ["SystemMessage"]
# The server imports many modules including:
# - Sentry SDK
# - Loguru with logger.remove()
# - Custom middleware and exception handlers
# - Various database and business logic modules
Technical Details
Observed Behavior
- The subprocess starts correctly (verified via
ps aux)
- Only the init
SystemMessage (subtype=init) is received
- Generator terminates after ~1 message instead of producing ~4 messages
- The subprocess continues running but Python never receives more stdout
What Was Ruled Out
- uvloop vs asyncio: Both event loops work in isolated context
- Prompt complexity: Simple prompts fail the same way as complex ones
- Sentry SDK: Disabling Sentry initialization doesn't fix the issue
- StreamingResponse: Even simple GET endpoints with regular responses fail
- Environment variables: Same ANTHROPIC_* variables used in both contexts
Root Cause Analysis
The issue appears to be related to how anyio's TextReceiveStream interacts with subprocess stdout in the context of the main server's configuration. The subprocess transport in claude_agent_sdk/_internal/transport/subprocess_cli.py uses:
async for line in self._stdout_stream:
# Process JSON lines...
This suggests the TextReceiveStream is not receiving all the data when the subprocess is spawned from within the Reportify codebase's context.
Server Configuration Differences
Main Server (Failing)
- Uses uvicorn with hot-reload
- Initializes Sentry SDK at module import
- Configures loguru with
logger.remove() at import time
- Loads many business logic modules
- Uses custom exception handlers and middleware
Minimal Server (Working)
- Basic FastAPI setup
- No Sentry, loguru, or complex middleware
- Direct uvicorn.run() call
Expected vs Actual Behavior
Expected: query() should return 4 messages:
- SystemMessage (init)
- AssistantMessage (with planning text content)
- AssistantMessage (continuation)
- ResultMessage (with cost/usage)
Actual: query() returns only 1 message:
- SystemMessage (init)
Impact
This bug makes the claude_agent_sdk unusable in FastAPI/uvicorn applications with typical middleware and initialization patterns, which is a common deployment scenario.
Additional Notes
- The issue is reproducible and consistent
- The subprocess CLI itself works correctly when invoked directly
- The problem appears to be in the Python-side subprocess communication layer
- This suggests a potential compatibility issue with FastAPI's event loop or middleware stack
Request
Could you investigate if there's an interaction between the subprocess transport and typical FastAPI server configurations (Sentry, logging, middleware)? A potential solution might involve making the subprocess transport more resilient to different async contexts or providing an alternative transport implementation.
Issue Summary
The
claude_agent_sdkquery() function works correctly in isolated tests but fails when called from within a FastAPI/uvicorn server. The SDK's subprocess transport only returns the initialSystemMessageand then terminates, never producing the expectedAssistantMessagewith response text.Environment
Reproduction Steps
Working Case (Isolated Test)
Working Case (Minimal FastAPI Server)
Failing Case (Reportify Codex Server)
Technical Details
Observed Behavior
ps aux)SystemMessage(subtype=init) is receivedWhat Was Ruled Out
Root Cause Analysis
The issue appears to be related to how
anyio'sTextReceiveStreaminteracts with subprocess stdout in the context of the main server's configuration. The subprocess transport inclaude_agent_sdk/_internal/transport/subprocess_cli.pyuses:This suggests the
TextReceiveStreamis not receiving all the data when the subprocess is spawned from within the Reportify codebase's context.Server Configuration Differences
Main Server (Failing)
logger.remove()at import timeMinimal Server (Working)
Expected vs Actual Behavior
Expected: query() should return 4 messages:
Actual: query() returns only 1 message:
Impact
This bug makes the
claude_agent_sdkunusable in FastAPI/uvicorn applications with typical middleware and initialization patterns, which is a common deployment scenario.Additional Notes
Request
Could you investigate if there's an interaction between the subprocess transport and typical FastAPI server configurations (Sentry, logging, middleware)? A potential solution might involve making the subprocess transport more resilient to different async contexts or providing an alternative transport implementation.