Fix query() stream hang when CLI subprocess dies mid-run#1111
Conversation
## Motivation Signed-off-by: anish <anishesg@users.noreply.github.com>
|
Thanks for jumping on this — I was investigating #1110 in parallel and ran your branch through some experiments; sharing them because two results seem important for review: 1. Both new tests pass on unfixed 2. The
The blocking |
|
yeah, good points. you're right that the tests don't actually hit the blocking case — they're testing the happy path where the consumer is actively reading. and the i think the stash-on-Query approach makes sense — lets us deliver the error reliably without racing the buffer state. lemme revert the |
|
yeah, good catch on both fronts. the tests aren't actually hitting the blocking case, and silently dropping errors under backpressure is definitely worse than hanging. gonna revert the |
Motivation
When the subprocess dies mid-run (e.g., when a Pro/Max subscription hits its rate-limit window and the CLI exits with code 1), the SDK logs "Fatal error in message reader" but the
query()async stream never terminates or raises, causing consumers awaiting the next message to hang indefinitely.The problem
In
_read_messages()(line 353 ofquery.py), when an exception occurs, the error handler attempts to send an error message to the stream usingawait self._message_send.send(). However, this await can block if the consumer has stopped iterating or the buffer is full. Thefinallyblock already handles the end sentinel correctly usingsend_nowait()with error suppression (line 372), but the error message path did not follow this pattern.What this does
Changes the error message send in
_read_messages()fromawait self._message_send.send()toself._message_send.send_nowait()wrapped insuppress(anyio.WouldBlock). This ensures subprocess fatal errors reach consumers without blocking, matching the pattern used for stream termination. If the buffer is full, the stream closes in the finally block and consumers seeEndOfStreamafter draining buffered messages.Adds regression tests that verify subprocess death during message iteration properly propagates exceptions to consumers without hanging, including tests for parked consumers blocked on receive.
Fixes #1110