fix: propagate CLI errors to pending control requests (#388)

## Summary

When the CLI exits with an error (e.g., invalid session ID passed to
`--resume`), signal all pending control requests immediately instead of
waiting for the 60-second timeout.

**The fix adds 4 lines** to the exception handler in `_read_messages`:

```python
# Signal all pending control requests so they fail fast instead of timing out
for request_id, event in list(self.pending_control_responses.items()):
    if request_id not in self.pending_control_results:
        self.pending_control_results[request_id] = e
        event.set()
```

## Problem

When the CLI exits with an error, the SDK's message reader catches it
but doesn't notify pending control requests. This causes `initialize()`
to wait for the full 60-second timeout even though the error is known
within seconds.

Example scenario:
1. User passes invalid session ID via
`ClaudeAgentOptions(resume="invalid-id")`
2. CLI prints `No conversation found with session ID: xxx` and exits
with code 1
3. SDK message reader catches the error after ~3 seconds
4. But `initialize()` keeps waiting for 60 seconds before timing out

## Solution

The existing code at `_send_control_request` lines 361-362 already
handles exceptions in results:
```python
if isinstance(result, Exception):
    raise result
```

The fix simply signals all pending control events when an error occurs,
allowing them to fail fast with the actual error instead of timing out.

## Test Plan

- [ ] Test with invalid session ID - should fail fast (~3s) instead of
timing out (60s)
- [ ] Test normal flow still works
- [ ] Test multiple pending requests all get signaled

Fixes #387
This commit is contained in:
Ramazan Rakhmatullin 2025-12-05 01:28:36 +03:00 committed by GitHub
parent 6791efec93
commit 69a310cc3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -214,6 +214,11 @@ class Query:
raise # Re-raise to properly handle cancellation
except Exception as e:
logger.error(f"Fatal error in message reader: {e}")
# Signal all pending control requests so they fail fast instead of timing out
for request_id, event in list(self.pending_control_responses.items()):
if request_id not in self.pending_control_results:
self.pending_control_results[request_id] = e
event.set()
# Put error in stream so iterators can handle it
await self._message_send.send({"type": "error", "error": str(e)})
finally: