{"record":{"id":"f900a6182ec91f11","repo":"PrefectHQ/fastmcp","slug":"server-session-was-closed-unexpectedly","errorCode":null,"errorMessage":"Server session was closed unexpectedly","messagePattern":"Server session was closed unexpectedly","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/client/client.py","lineNumber":833,"sourceCode":"        # Only passed when this client actually wants non-default settings, so an\n        # ordinary client never sends an argument a transport might not accept.\n        if transport_options is not None:\n            connection = self.transport.connect_session(\n                transport_options=transport_options, **self._session_kwargs\n            )\n        else:\n            connection = self.transport.connect_session(**self._session_kwargs)\n\n        with catch(get_catch_handlers()):\n            async with connection as session:\n                self._session_state.session = session\n                # Initialize the session if auto_initialize is enabled\n                try:\n                    if self.auto_initialize:\n                        await self._negotiate()\n                    yield\n                except anyio.ClosedResourceError as e:\n                    raise RuntimeError(\"Server session was closed unexpectedly\") from e\n                finally:\n                    self._reset_session_state()\n\n    async def _negotiate(\n        self,\n        timeout: datetime.timedelta | float | int | None = None,\n    ) -> None:\n        \"\"\"Run the connect-time protocol negotiation dictated by ``self.mode``.\n\n        - ``\"legacy\"``: today's initialize handshake (populates ``initialize_result``).\n        - ``\"auto\"``: probe ``server/discover`` at the newest modern version and adopt it,\n          denylist-falling-back to the initialize handshake for handshake-era servers.\n        - a modern version string: adopt that version directly (from ``prior_discover`` if\n          supplied, else a synthesized minimal ``DiscoverResult``).\n\n        Idempotent: once the session has a negotiated protocol version, this is a no-op.\n        \"\"\"\n        if self.session.protocol_version is not None:","sourceCodeStart":815,"sourceCodeEnd":851,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/client/client.py#L815-L851","documentation":"While the client's session runner is active, a transport stream closed by the remote side (anyio.ClosedResourceError) is converted into this RuntimeError. It means the server terminated the connection mid-session rather than the client closing it. The original ClosedResourceError is chained as the cause.","triggerScenarios":"The server process exits, crashes, or closes the transport (stdio pipe break, dropped HTTP/SSE stream, killed subprocess) while the client is inside `async with client:` and issues a request.","commonSituations":"Server crashes under load or on an unhandled exception; stdio server's stdout closes killing the subprocess; a proxy/gateway times out and terminates the stream; OS kills the server process.","solutions":["Check server logs for a crash or unhandled exception at disconnect time","Verify the transport target: command path/env for stdio, URL/auth for remote servers","Add retry/reconnect logic around the client session for long-lived connections","Ensure server/client protocol versions are compatible"],"exampleFix":"// before\nasync with client:\n    result = await client.call_tool(\"run\", {})  # RuntimeError if server died\n\n// after\nfor attempt in range(3):\n    try:\n        async with client:\n            result = await client.call_tool(\"run\", {})\n        break\n    except RuntimeError as e:\n        if \"closed unexpectedly\" not in str(e) or attempt == 2:\n            raise","handlingStrategy":"retry","validationCode":"import shutil\nassert shutil.which(server_command) is not None, \"server executable missing\"","typeGuard":null,"tryCatchPattern":"try:\n    async with client:\n        result = await client.call_tool(\"run\", {})\nexcept RuntimeError as e:\n    if \"closed unexpectedly\" in str(e):\n        logger.error(\"server died; cause=%r\", e.__cause__)\n    else:\n        raise","preventionTips":["Monitor server process health and logs","Add reconnect/backoff around long-lived client sessions","Verify transport URLs, commands, and auth before connecting"],"tags":["python","network","transport","server-crash"],"backgroundTag":"connection-closed-by-peer","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}