Skip to content

fix: preserve HTTP status codes in streamable HTTP transport error responses#2176

Closed
giulio-leone wants to merge 2 commits intomodelcontextprotocol:mainfrom
giulio-leone:fix/http-transport-error-propagation
Closed

fix: preserve HTTP status codes in streamable HTTP transport error responses#2176
giulio-leone wants to merge 2 commits intomodelcontextprotocol:mainfrom
giulio-leone:fix/http-transport-error-propagation

Conversation

@giulio-leone
Copy link

Problem

The streamable HTTP transport has two error propagation issues that cause clients to hang or receive uninformative error messages:

1. HTTP status codes are discarded in error responses

Non-2xx responses produce generic error messages that lose the original HTTP status:

  • 404ErrorData(code=INVALID_REQUEST, message="Session terminated")
  • 401/403/500/...ErrorData(code=INTERNAL_ERROR, message="Server returned an error response")

Callers cannot distinguish auth errors (401/403) from server errors (500) or other failures.

2. Transport errors cause caller to hang

Connection-level errors (timeouts, DNS failures, refused connections) raised inside _handle_post_request are not caught. When called via tg.start_soon() for JSONRPCRequest messages, the exception propagates to the task group but no JSONRPCError is written to the read stream — the caller blocks indefinitely.

Fix

Status code preservation

Consolidated the separate 404 and >= 400 handlers into a single block that:

  1. Reads the response body for error detail
  2. Includes HTTP {status}: {body} in the error message

Transport error propagation

Wrapped _handle_post_request in a try/except that:

  1. Catches any exception during the HTTP request
  2. Sends a JSONRPCError through the read_stream_writer so the caller gets an error instead of hanging
  3. Re-raises the exception so the task group can handle it

Test

13 fast tests pass (validation, JSON response, content type, session), 2 consecutive clean runs. Integration tests not affected (verified test_streamable_http_client_json_response passes).

Fixes #2110

…sponses

The streamable HTTP transport had two error propagation issues:

1. Non-2xx responses used generic error messages ('Session terminated',
   'Server returned an error response') that discarded the HTTP status
   code, making it impossible to distinguish auth errors (401/403) from
   server errors (500) or not-found (404).

2. Connection/transport errors (timeouts, DNS failures, connection
   refused) raised inside _handle_post_request were not caught, causing
   callers to hang indefinitely waiting for a response on the read
   stream.

Fix:
- Consolidate 404 and general >= 400 handlers into a single block that
  reads the response body and includes 'HTTP {status}: {body}' in the
  error message
- Wrap _handle_post_request in try/except to catch transport errors and
  forward them as JSONRPCError through the read stream before re-raising

Fixes modelcontextprotocol#2110
@giulio-leone giulio-leone force-pushed the fix/http-transport-error-propagation branch from 8d879bb to 0176b50 Compare February 28, 2026 14:41
@giulio-leone
Copy link
Author

Closing — the test failures indicate this branch has diverged too far from main. The HTTP status code preservation approach needs to be reworked to align with recent upstream changes.

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.

HTTP transport swallows non-2xx status codes causing client to hang

1 participant