fix(streamable-http): surface transport faults to callers and message_handler - #3318
fix(streamable-http): surface transport faults to callers and message_handler#3318functionxX wants to merge 4 commits into
Conversation
…_handler When a streamable-http response stream dies with a transport error, the exception was swallowed: the pending request got a generic CONNECTION_CLOSED error and the fault never reached message_handler. Forward the exception into the read stream as an Exception item and carry it into the synthesized error messages on both the immediate and reconnect-exhausted paths.
|
Thanks for the contribution. This repository only keeps pull requests open when they're linked to an issue that a maintainer has assigned to the author — CONTRIBUTING.md explains why and how we work. This PR has been closed for now because you aren't currently assigned to #1401. If a maintainer would like this change as a PR from you, they'll assign you to #1401 and this PR will reopen automatically — there's nothing more you need to do. (If you opened the issue, this PR already shows up on its timeline.) There's no need to open a new PR — this one will be reopened. While it's closed, please push any updates as new commits rather than force-pushing, since GitHub can't reopen a PR whose branch has been rewritten. Maintainers: reopening this PR, removing the |
Fixes #1401
What changed
When a streamable-http response stream dies with a transport error (e.g.
httpx.ReadTimeoutfromsse_read_timeout), the original exception wasswallowed:
CONNECTION_CLOSEDerror("SSE stream ended without a response" / "reconnection attempts were
exhausted"), hiding the root cause from user code.
message_handler, even though theIncomingMessagecontract says transport-level exceptions are delivered there.
Now:
_handle_sse_responsecaptures the stream exception and forwards it into theread stream as an
Exceptionitem, so the session'smessage_handlerobserves the fault (custom handlers, like the one the reporter built as a
workaround, now receive it). The synthesized error keeps code
CONNECTION_CLOSEDand carries the exception in its message._handle_reconnectioncarries the latest failure through the retry loop andincludes it in the exhausted-budget error message.
_forward_stream_faulthelper, best-effort like_resolve_abandoned_request.How verified
test_a_non_resumable_sse_drop_resolves_the_request_with_an_errorand
test_exhausted_reconnection_attempts_resolve_the_request_with_an_errorto assert the fault item and the exception in the error message.
test_an_id_bearing_stream_that_dies_surfaces_the_fault_and_resolves_the_requestcovering the reconnect-exhausted path end to end.
pytest tests/client: 703 passed, 8 skipped, 1 xfailed; one unrelatedpre-existing failure in
test_transport_stream_cleanup.py(also fails onclean main on this machine).
ruff check,ruff format, andpyrightclean.Disclosure: this contribution was written with AI assistance and personally reviewed and tested by me.