Skip to content

fix: surface prompt stream errors instead of hanging query() forever#1109

Open
Echolonius wants to merge 1 commit into
anthropics:mainfrom
Echolonius:fix/stream-input-error-surfacing
Open

fix: surface prompt stream errors instead of hanging query() forever#1109
Echolonius wants to merge 1 commit into
anthropics:mainfrom
Echolonius:fix/stream-input-error-surfacing

Conversation

@Echolonius

Copy link
Copy Markdown

Fixes #1108.

Problem

stream_input() caught every exception from the caller's prompt AsyncIterable (or a failed stdin write / json.dumps) and logged it at debug level. Stdin stayed open, the CLI kept waiting for input that would never come, no result ever arrived, and query() blocked forever with no surfaced error. Verified against the real CLI: a generator that raises hangs query() indefinitely whether it raises before the first message or between messages.

Fix

In the exception handler, when not already in teardown (self._closed):

  1. log at error level instead of debug,
  2. close stdin (end_input(), best-effort) so the CLI can wind down instead of waiting on input,
  3. push an {"type": "error"} message into the message stream — the same mechanism _read_messages already uses for fatal read errors — so receive_messages() raises promptly.

During teardown the old debug-and-return behavior is preserved (writes interrupted by close() are expected noise, not actionable errors). If the read task already closed the stream, its error wins (ClosedResourceError suppressed).

Verification

  • Real CLI (SDK 0.2.116, CLI 2.1.207): both repro variants from Exception in the prompt AsyncIterable is swallowed at debug level and query() hangs forever #1108 hung for the full 45s watchdog on main; with this fix they raise Error streaming input: boom-... in 3-7s.
  • New regression test test_streaming_prompt_input_stream_error_surfaces: mock transport whose stdout produces nothing on its own (like the real CLI mid-wait); prompt generator yields one message then raises. Fails on main (bounded 5s timeout — the hang); passes with the fix, and asserts end_input() was called.
  • pytest tests/test_query.py tests/test_tool_callbacks.py tests/test_streaming_client.py: 137 passed. ruff check and ruff format --check clean.

Note: independent of #1106 (different hunks in the same file; no overlap).

🤖 Generated with Claude Code

https://claude.ai/code/session_01YcMi8ny9DUBDf6y6m76HWk

stream_input() caught every exception from the caller's prompt
AsyncIterable (or a failed stdin write) and logged it at debug level.
Stdin stayed open, the CLI kept waiting for input that would never
come, no result ever arrived, and query() blocked forever with no
surfaced error.

On failure (outside teardown): log at error level, close stdin so the
CLI can wind down, and push an error message into the message stream so
receive_messages() raises instead of blocking indefinitely.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exception in the prompt AsyncIterable is swallowed at debug level and query() hangs forever

1 participant