Skip to content

fix: handle ClosedResourceError in _handle_message error recovery path#2072

Open
BabyChrist666 wants to merge 1 commit intomodelcontextprotocol:mainfrom
BabyChrist666:fix/handle-closed-resource-in-error-handler-2064
Open

fix: handle ClosedResourceError in _handle_message error recovery path#2072
BabyChrist666 wants to merge 1 commit intomodelcontextprotocol:mainfrom
BabyChrist666:fix/handle-closed-resource-in-error-handler-2064

Conversation

@BabyChrist666
Copy link
Contributor

Summary

Closes #2064

When a client disconnects while a stateless streamable-HTTP server is processing a request, the error handler in _handle_message tries to send_log_message() back to the client. Since the session was already terminated and the write stream closed, this raises ClosedResourceError, which is unhandled and crashes the stateless session with an ExceptionGroup.

This is a different code path from what PR #1384 fixed. That PR addressed ClosedResourceError in the message router loop. This bug is in the error recovery path: catch exception → try to log it to client → write stream already closed → crash.

Fix

Wrap the send_log_message() call in _handle_message's exception handler with a try/except that catches anyio.ClosedResourceError and anyio.BrokenResourceError. Failing to notify a disconnected client is expected and harmless — we just log it at debug level and move on.

# Before: crashes if client disconnected
await session.send_log_message(level="error", ...)

# After: gracefully handles disconnected client
try:
    await session.send_log_message(level="error", ...)
except (anyio.ClosedResourceError, anyio.BrokenResourceError):
    logger.debug("Could not send error log: client disconnected")

This follows the same pattern already used elsewhere in the codebase (session.py:406, streamable_http.py:587, streamable_http.py:1009, etc.).

Test plan

  • Added 4 new tests in test_lowlevel_exception_handling.py:
    • test_exception_handling_with_disconnected_client[ClosedResourceError]
    • test_exception_handling_with_disconnected_client[BrokenResourceError]
    • test_exception_handling_with_disconnected_client_raise_exceptions[ClosedResourceError]
    • test_exception_handling_with_disconnected_client_raise_exceptions[BrokenResourceError]
  • All 10 existing + new tests pass
  • ruff lint + format clean
  • pyright clean (0 errors)

🤖 Generated with Claude Code

…ontextprotocol#2064)

When a client disconnects mid-request, the exception handler in
_handle_message tries to send_log_message() back to the client. Since
the write stream is already closed, this raises ClosedResourceError,
which crashes the stateless session with an ExceptionGroup.

Wrap the send_log_message call in a try/except that catches
ClosedResourceError and BrokenResourceError, since failing to notify
a disconnected client is expected and harmless.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

ClientDisconnect during _handle_post_request crashes stateless session with ClosedResourceError

1 participant