Skip to content

fix(streamable_http): send response on ClientDisconnect to satisfy middleware chains#5

Merged
wiggzz merged 2 commits intodbt-labs/patched-1.27.0from
wj/DI-3218-clientdisconnect-send-response
May 5, 2026
Merged

fix(streamable_http): send response on ClientDisconnect to satisfy middleware chains#5
wiggzz merged 2 commits intodbt-labs/patched-1.27.0from
wj/DI-3218-clientdisconnect-send-response

Conversation

@wiggzz
Copy link
Copy Markdown

@wiggzz wiggzz commented May 5, 2026

Problem

When ClientDisconnect is caught in _handle_post_request, returning early without sending a response works fine for bare ASGI apps, but ai-codegen-api wraps the MCP transport in Starlette BaseHTTPMiddleware which raises RuntimeError('No response returned') when the ASGI app doesn't call send.

The existing ClientDisconnectHandlerMiddleware can't catch this because BaseHTTPMiddleware layers wrap above it in the middleware chain.

Solution

Send a 499 (Client Closed Request) response when ClientDisconnect is caught. The ASGI server (uvicorn) will simply drop the response if the socket is already closed, but the middleware chain sees a response was sent and doesn't error.

Uses 499 (nginx convention for "Client Closed Request") rather than 202 — more semantically correct for a cancelled request. Builds the Response directly to avoid the HTTPStatus type hint limitation (499 is not in Python's stdlib HTTPStatus enum).

Tests

Updated 2 existing tests in tests/server/streamable_http/test_client_disconnect_post.py:

  • test_client_disconnect_sends_response (renamed from does_not_send_response) — verifies 499 response is sent
  • test_client_disconnect_writer_suppresses_errors — verifies response is still sent even when writer is broken

Upstream context

Inspired by upstream PR modelcontextprotocol/python-sdk#1647 which also uses 499 for this case. Tracking issue: modelcontextprotocol/python-sdk#1648.

wiggzz added 2 commits May 5, 2026 13:38
…ddleware chains

When ClientDisconnect is caught in _handle_post_request, send a 202 Accepted
response so that Starlette BaseHTTPMiddleware wrappers don't raise
RuntimeError('No response returned'). The ASGI server will drop the response
if the socket is already closed.

Fixes ai-codegen-api 'No response returned' error when client disconnects
mid-POST. The existing ClientDisconnectHandlerMiddleware can no longer
intercept the early return since BaseHTTPMiddleware wraps above it.
…for ClientDisconnect

499 is the nginx convention for 'Client Closed Request' — more semantically
correct than 202 Accepted for a cancelled request.
@wiggzz wiggzz merged commit 55a042a into dbt-labs/patched-1.27.0 May 5, 2026
1 of 18 checks passed
@wiggzz wiggzz deleted the wj/DI-3218-clientdisconnect-send-response branch May 5, 2026 19:14
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.

1 participant