Skip to content

Commit 08afa6a

Browse files
rudi193-cmdclaude
andcommitted
fix(session): drop unknown-ID responses instead of crashing session
When a response arrives for a request ID that has no pending waiter (e.g. the request timed out and was removed from _response_streams), the previous code called _handle_incoming(RuntimeError(...)), which routed through _default_message_handler, which now re-raises on any Exception — killing the session and sending CONNECTION_CLOSED to all remaining in-flight requests. The unknown-ID case is non-fatal: the late response simply has nowhere to go. Log a warning and drop it; don't propagate through the message handler. This matches the intention of the existing warning log in _normalize_request_id and keeps the session alive for subsequent work. Also updated the #1401 test module docstring to clarify that protocol-level non-fatal errors are handled inline, not via _default_message_handler. Fixes regressions in: - test_notification_validation_error (test_88_random_error.py) - test_response_id_non_numeric_string_no_match (test_session.py) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent cb53075 commit 08afa6a

2 files changed

Lines changed: 9 additions & 4 deletions

File tree

src/mcp/shared/session.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ async def _handle_response(self, message: SessionMessage) -> None:
516516
if stream:
517517
await stream.send(message.message)
518518
else:
519-
await self._handle_incoming(RuntimeError(f"Received response with an unknown request ID: {message}"))
519+
logging.warning("Received response with unknown request ID %r — dropping (request may have timed out)", response_id)
520520

521521
async def _received_request(self, responder: RequestResponder[ReceiveRequestT, SendResultT]) -> None:
522522
"""Can be overridden by subclasses to handle a request without needing to

tests/issues/test_1401_client_session_error_handling.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@
55
`continue`, waiting for the next message that never came — hanging all pending
66
requests indefinitely.
77
8-
Fix: raise when message is an Exception so _receive_loop's except-handler runs,
9-
triggering the finally block that closes pending response streams with
10-
CONNECTION_CLOSED.
8+
Fix: _default_message_handler re-raises when the message is an Exception
9+
(transport errors from the stream). This propagates out of _receive_loop's
10+
async-for, triggering the finally block that closes all pending response streams
11+
with CONNECTION_CLOSED — unblocking any in-flight callers.
12+
13+
Protocol-level non-fatal errors (e.g. responses with unknown request IDs from
14+
timed-out requests) are handled inline in _handle_response with a warning log,
15+
so they do not reach _default_message_handler and cannot kill the session.
1116
"""
1217

1318
import anyio

0 commit comments

Comments
 (0)