Skip to content

fix(streamable-http): surface transport faults to callers and message_handler - #3318

Open
functionxX wants to merge 4 commits into
modelcontextprotocol:mainfrom
functionxX:fix/1401-surface-transport-exceptions
Open

fix(streamable-http): surface transport faults to callers and message_handler#3318
functionxX wants to merge 4 commits into
modelcontextprotocol:mainfrom
functionxX:fix/1401-surface-transport-exceptions

Conversation

@functionxX

Copy link
Copy Markdown

Fixes #1401

What changed

When a streamable-http response stream dies with a transport error (e.g.
httpx.ReadTimeout from sse_read_timeout), the original exception was
swallowed:

  • The pending request was resolved with a generic CONNECTION_CLOSED error
    ("SSE stream ended without a response" / "reconnection attempts were
    exhausted"), hiding the root cause from user code.
  • The fault never reached message_handler, even though the IncomingMessage
    contract says transport-level exceptions are delivered there.

Now:

  • _handle_sse_response captures the stream exception and forwards it into the
    read stream as an Exception item, so the session's message_handler
    observes the fault (custom handlers, like the one the reporter built as a
    workaround, now receive it). The synthesized error keeps code
    CONNECTION_CLOSED and carries the exception in its message.
  • _handle_reconnection carries the latest failure through the retry loop and
    includes it in the exhausted-budget error message.
  • New _forward_stream_fault helper, best-effort like
    _resolve_abandoned_request.

How verified

  • Updated test_a_non_resumable_sse_drop_resolves_the_request_with_an_error
    and test_exhausted_reconnection_attempts_resolve_the_request_with_an_error
    to assert the fault item and the exception in the error message.
  • Added test_an_id_bearing_stream_that_dies_surfaces_the_fault_and_resolves_the_request
    covering the reconnect-exhausted path end to end.
  • pytest tests/client: 703 passed, 8 skipped, 1 xfailed; one unrelated
    pre-existing failure in test_transport_stream_cleanup.py (also fails on
    clean main on this machine). ruff check, ruff format, and pyright clean.

Disclosure: this contribution was written with AI assistance and personally reviewed and tested by me.

…_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.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Re-trigger cubic

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.

ClientSession Error Handling

1 participant