{"record":{"id":"f74611184e823be5","repo":"PrefectHQ/fastmcp","slug":"clientgroup-client-for-server-route-server-name-r","errorCode":null,"errorMessage":"ClientGroup client for server {route.server_name!r} is not connected","messagePattern":"ClientGroup client for server (.+?) is not connected","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/client/group.py","lineNumber":148,"sourceCode":"        stack = self._exit_stack\n        self._exit_stack = None\n        self._tool_routes.clear()\n        self._catalog_loaded = False\n        if stack is not None:\n            return await stack.__aexit__(exc_type, exc_value, traceback)\n        return None\n\n    def _require_connected(self) -> None:\n        disconnected = [\n            name for name, client in self._clients.items() if not client.is_connected()\n        ]\n        if disconnected:\n            names = \", \".join(repr(name) for name in disconnected)\n            raise RuntimeError(f\"ClientGroup clients are not connected: {names}\")\n\n    def _require_route_connected(self, route: ToolRoute) -> None:\n        if not route.client.is_connected():\n            raise RuntimeError(\n                f\"ClientGroup client for server {route.server_name!r} is not connected\"\n            )\n\n    async def list_tools(\n        self, *, cache_mode: CacheMode = \"refresh\"\n    ) -> list[mcp_types.Tool]:\n        \"\"\"List tools from every client with namespaced names.\n\n        An explicit call is the group's catalog-refresh mechanism, so it\n        defaults to `cache_mode=\"refresh\"`: a client-side response cache\n        (SEP-2549) is repopulated rather than served, and the routes reflect\n        what every server advertises now. Pass `cache_mode=\"use\"` to allow\n        cache hits when staleness within the server's hint is acceptable.\n        \"\"\"\n        self._require_connected()\n        tools: list[mcp_types.Tool] = []\n        routes: dict[str, ToolRoute] = {}\n        clients = list(self._clients.items())","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/client/group.py#L130-L166","documentation":"ClientGroup raises this RuntimeError in resolve_tool when the route for the requested tool is known, but the specific client (server) that advertises that tool is no longer connected. Unlike error 90, other servers in the group may still be healthy — the group deliberately scopes the failure to the one dead server instead of failing everything. The server name is interpolated into the message so you know which backend to restore.","triggerScenarios":"Calling group.call_tool(name, ...) or group.call_tool_mcp(name, ...) where `name` was previously discovered via list_tools, but the owning client's session dropped (stdio subprocess exited, remote connection closed, context exited) between discovery and the call. Also raised when a user manually enters only some clients' contexts and then calls a tool owned by an unentered one.","commonSituations":"A stdio MCP server process crashed after tools were listed; a remote HTTP/SSE server timed out or restarted; long-running agent loops holding a group whose context was exited by an outer scope; partial manual connection management where one client was never entered.","solutions":["Reconnect the named server: re-enter its client context, or recreate and re-enter the ClientGroup with `async with`, which reconnects all clients concurrently.","If the group's context exited, re-enter it (`async with group` is only valid on a fresh group — create a new ClientGroup after __aexit__) and call list_tools() to rebuild routes.","For flaky remote servers, wrap the call in a retry that recreates the group on this RuntimeError.","Check `group.clients[server_name].is_connected()` before calling tools owned by that server during shutdown or restart windows."],"exampleFix":"# before\nroute_owner = \"weather\"\nresult = await group.call_tool(\"weather_get_forecast\", {\"city\": \"SF\"})\n# RuntimeError: ClientGroup client for server 'weather' is not connected\n\n# after\ntry:\n    result = await group.call_tool(\"weather_get_forecast\", {\"city\": \"SF\"})\nexcept RuntimeError as e:\n    if \"is not connected\" in str(e):\n        async with ClientGroup.from_config(config) as group:\n            await group.list_tools()\n            result = await group.call_tool(\"weather_get_forecast\", {\"city\": \"SF\"})","handlingStrategy":"retry","validationCode":"server = \"weather\"\nif not group.clients[server].is_connected():\n    raise RuntimeError(f\"Server {server!r} is down; reconnect before calling its tools\")","typeGuard":null,"tryCatchPattern":"try:\n    result = await group.call_tool(name, args)\nexcept RuntimeError as e:\n    if \"is not connected\" in str(e):\n        group = ClientGroup.from_config(config)\n        async with group:\n            await group.list_tools()\n            result = await group.call_tool(name, args)\n    else:\n        raise","preventionTips":["Monitor per-server health (`client.is_connected()`) since one dead server only affects its own tools","Use supervised stdio transports that restart crashed subprocesses","Wrap long-lived agent loops so the group context spans the whole loop","Add retry-on-RuntimeError with group recreation for remote servers prone to drops"],"tags":["mcp","connection","client-group","session-dropped"],"backgroundTag":"client-not-connected","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}