Skip to content

Client._disconnect() needs timeout — hangs indefinitely on stale HTTP connections #3947

@godwinbw-egm

Description

@godwinbw-egm

Problem

When using FastMCPToolset with StreamableHttpTransport for long-lived processes (eval suites, test runners), Client._disconnect() can hang indefinitely when the remote MCP server has dropped the HTTP keep-alive connection.

Root Cause

_disconnect() awaits self._session_state.session_task which calls _session_runner. The runner's AsyncExitStack.__aexit__ triggers a POST to the server for session termination. When the server connection is stale (idle timeout after 60-90s), this POST hangs until httpx.ReadTimeout fires — but there's no bounded deadline on the await session_task itself.

Exception Chain

RuntimeError: Client is not connected
  caused by ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)
    CancelledError: Cancelled via cancel scope
      WouldBlock

Impact

In our eval suite (15 scenarios, sequential), after a long-running scenario (80-140s), the next call to client._disconnect() hangs. This poisons all subsequent scenarios because:

  1. _disconnect() hangs waiting for session_task
  2. New client instances share the httpx connection pool with the orphaned client
  3. list_tools() / call_tool() fail with "Client is not connected"

Workaround

We wrapped the __aexit__ call in asyncio.wait_for(..., timeout=3.0) and force-null the client on timeout, then gc.collect().

Suggested Fix

Add a timeout parameter to Client._disconnect():

async def _disconnect(self, timeout: float = 10.0) -> None:
    if self._session_state.session_task is not None:
        try:
            await asyncio.wait_for(self._session_state.session_task, timeout=timeout)
        except (TimeoutError, asyncio.TimeoutError):
            self._session_state.session_task.cancel()
            # ... cleanup

Environment

  • fastmcp version: latest (via pip)
  • Python: 3.13.5
  • pydantic-ai: 1.60.0
  • Transport: StreamableHttpTransport
  • Server: Remote HTTP MCP servers (Fly.io hosted)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working. Reports of errors, unexpected behavior, or broken functionality.clientRelated to the FastMCP client SDK or client-side functionality.httpRelated to HTTP transport, networking, or web server functionality.needs MREBug reports require a copy/pastable MRE that can run as-is.too-longExcessively verbose or unedited LLM output. Condense before triage.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions