PrefectHQ/fastmcp · error · RuntimeError
ClientGroup client for server {route.server_name!r} is not c
Error message
ClientGroup client for server {route.server_name!r} is not connected What it means
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.
Source
Thrown at fastmcp_slim/fastmcp/client/group.py:148
stack = self._exit_stack
self._exit_stack = None
self._tool_routes.clear()
self._catalog_loaded = False
if stack is not None:
return await stack.__aexit__(exc_type, exc_value, traceback)
return None
def _require_connected(self) -> None:
disconnected = [
name for name, client in self._clients.items() if not client.is_connected()
]
if disconnected:
names = ", ".join(repr(name) for name in disconnected)
raise RuntimeError(f"ClientGroup clients are not connected: {names}")
def _require_route_connected(self, route: ToolRoute) -> None:
if not route.client.is_connected():
raise RuntimeError(
f"ClientGroup client for server {route.server_name!r} is not connected"
)
async def list_tools(
self, *, cache_mode: CacheMode = "refresh"
) -> list[mcp_types.Tool]:
"""List tools from every client with namespaced names.
An explicit call is the group's catalog-refresh mechanism, so it
defaults to `cache_mode="refresh"`: a client-side response cache
(SEP-2549) is repopulated rather than served, and the routes reflect
what every server advertises now. Pass `cache_mode="use"` to allow
cache hits when staleness within the server's hint is acceptable.
"""
self._require_connected()
tools: list[mcp_types.Tool] = []
routes: dict[str, ToolRoute] = {}
clients = list(self._clients.items())View on GitHub (pinned to 1f02114297)
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.
Example fix
# before
route_owner = "weather"
result = await group.call_tool("weather_get_forecast", {"city": "SF"})
# RuntimeError: ClientGroup client for server 'weather' is not connected
# after
try:
result = await group.call_tool("weather_get_forecast", {"city": "SF"})
except RuntimeError as e:
if "is not connected" in str(e):
async with ClientGroup.from_config(config) as group:
await group.list_tools()
result = await group.call_tool("weather_get_forecast", {"city": "SF"}) Defensive patterns
Strategy: retry
Validate before calling
server = "weather"
if not group.clients[server].is_connected():
raise RuntimeError(f"Server {server!r} is down; reconnect before calling its tools") Try / catch
try:
result = await group.call_tool(name, args)
except RuntimeError as e:
if "is not connected" in str(e):
group = ClientGroup.from_config(config)
async with group:
await group.list_tools()
result = await group.call_tool(name, args)
else:
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ClientGroup clients are not connected: {names}
- Tool name collision: {public_name!r}
- Unknown tool: {name!r}. If servers changed their tools, call
- INVALID_PARAMS
- The client negotiated a modern protocol era (server/discover
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/f74611184e823be5.
Report an issue: GitHub.