fix(server): terminate streamable HTTP sessions on manager shutdown

Closed
BA
2mo ago
+129-2

Summary

Fixes #2150 on current main.

When shutting down a Streamable HTTP server (CTRL+C / lifespan exit) with live SSE clients, uvicorn logs:

ERROR: ASGI callable returned without completing response.

Root causes (still present on main @ a4f4ccd)

  1. StreamableHTTPSessionManager.run() finally cancelled the task group and cleared _server_instances without await transport.terminate() on active sessions.
  2. StreamableHTTPServerTransport.terminate() closed request/read/write streams but left _sse_stream_writers open, so EventSourceResponse never completed.

Fix

  • Manager shutdown: terminate each non-terminated transport before cancelling the task group.
  • terminate(): close all SSE writers first via close_sse_stream (covers POST request streams, GET standalone, and provisional replay keys).
  • Register the GET standalone SSE writer immediately; use a provisional writer key during event replay so mid-replay shutdown can still close the ASGI response.

Relation to other work

  • Earlier Atlas PR #3125 landed the same approach with tests, then was closed unmerged during v2 backlog cleanup with a note to track via #2253 and reopen if still relevant on main.
  • #2253 targets v1.x, has no regression tests, and is stale (last update 2026-04-25). This PR is the main + tests line maintainers invited.
  • Happy to close/adjust if maintainers prefer a different landing path.

Verification

uv run pytest tests/server/test_streamable_http_manager.py::test_terminate_closes_active_sse_stream_writers \
  tests/server/test_streamable_http_manager.py::test_manager_shutdown_terminates_active_sessions \
  tests/server/test_streamable_http_manager.py::test_terminate_closes_standalone_get_sse_writer_when_registered \
  tests/server/test_streamable_http_manager.py::test_terminate_closes_provisional_replay_sse_writer -q
# 4 passed

uv run pytest tests/server/test_streamable_http_manager.py -q
# 34 passed

Test plan

  • New unit regressions for terminate + manager shutdown
  • Full test_streamable_http_manager.py suite green locally (34)
  • CI on this PR