Skip to content

[BUG] Stateless session crashes with ClosedResourceError when client disconnects mid-request #1852

@davidbolvansky-invicti

Description

@davidbolvansky-invicti

Checks

  • I have updated to the lastest minor and patch version of Strands
  • I have checked the documentation and this is not expected behavior
  • I have searched ./issues and there are no duplicates of my issue

Strands Version

1.26.0

Python Version

3.14

Operating System

Linux (Debian-based, Docker container)

Installation Method

pip

Steps to Reproduce

Description:

When using stateless_http=True with json_response=True, client disconnections cause _handle_message to crash. The exception handler at lowlevel/server.py:699-705
catches the stream exception but then calls session.send_log_message(), which writes to the already-closed _write_stream. This raises anyio.ClosedResourceError,
which propagates as an ExceptionGroup through the task group and kills the session.

Reproduction:

  1. Create a server with FastMCP(stateless_http=True, json_response=True)
  2. Register a tool that takes a few seconds to execute
  3. Have the client disconnect (close HTTP connection) while the server is processing or during cleanup
  4. Server logs 4 errors: Error handling POST request, Received exception from stream, Stateless session crashed

Stack trace:

  ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
    mcp/server/lowlevel/server.py:701 _handle_message
      → session.send_log_message(level="error", data="Internal Server Error", ...)
    mcp/server/session.py:213 send_log_message
      → send_notification()
    mcp/shared/session.py:335 send_notification
      → self._write_stream.send(session_message)
    anyio/streams/memory.py:218 send_nowait
      → raise ClosedResourceError

Root cause:

In lowlevel/server.py:699-705:

  case Exception():
      logger.error(f"Received exception from stream: {message}")
      await session.send_log_message(  # ← crashes here
          level="error",
          data="Internal Server Error",
          logger="mcp.server.exception_handler",
      )

The send_log_message call is not guarded against ClosedResourceError / BrokenResourceError. When the client has already disconnected, the write stream is closed,
and the notification delivery fails.

Impact:

Under concurrent load (multiple clients), client disconnections cause noisy error logs and kill sessions that are already in teardown. The crash is caught by
streamable_http_manager.py:187 (except Exception: logger.exception("Stateless session crashed")), so it doesn't bring down the server process, but it disrupts
the session's task group and any sibling tasks within it.

Expected Behavior

When a client disconnects, the server should gracefully close the session without raising an unhandled exception. The send_log_message call in the exception
handler should be guarded against write failures — if the client is already gone, there's no one to receive the notification, so the error should be silently
discarded (or logged at DEBUG level).

Actual Behavior

The server crashes the stateless session with an unhandled ExceptionGroup containing anyio.ClosedResourceError. The exception handler at
lowlevel/server.py:699-705 attempts to send a log notification to the disconnected client, which fails because the write stream is already closed. This
propagates through the task group, killing the session and producing 4 error-level log lines (Error handling POST request, Received exception from stream,
Stateless session crashed) per disconnected request.

Additional Context

No response

Possible Solution

Wrap the send_log_message call in a try/except:

  case Exception():
      logger.error(f"Received exception from stream: {message}")
      try:
          await session.send_log_message(
              level="error",
              data="Internal Server Error",
              logger="mcp.server.exception_handler",
          )
      except (anyio.ClosedResourceError, anyio.BrokenResourceError):
          pass  # Client already disconnected, nothing to notify

Related Issues

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions