PrefectHQ/fastmcp · error · MCPError

408

408

Error message

Timed out while waiting for response.

What it means

When batching requests over an HTTP transport, a leaf `httpx.ConnectTimeout` inside an exception group is converted into an MCPError with code 408 (Request Timeout). This normalizes low-level HTTP connect timeouts into a single recognizable MCP error so callers can detect 'server unreachable / too slow' uniformly.

Source

Thrown at fastmcp_slim/fastmcp/utilities/exceptions.py:59

def is_request_error(exc: BaseException) -> bool:
    """Return whether an exception is an httpx2 or legacy-httpx request error."""
    return isinstance(exc, httpx2.RequestError) or _is_legacy_httpx_exception(
        exc, "RequestError"
    )


def iter_exc(group: BaseExceptionGroup):
    for exc in group.exceptions:
        if isinstance(exc, BaseExceptionGroup):
            yield from iter_exc(exc)
        else:
            yield exc


def _exception_handler(group: BaseExceptionGroup):
    for leaf in iter_exc(group):
        if isinstance(leaf, httpx2.ConnectTimeout):
            raise MCPError(
                code=httpx2.codes.REQUEST_TIMEOUT,
                message="Timed out while waiting for response.",
            )
        raise leaf


# this catch handler is used to catch taskgroup exception groups and raise the
# first exception. This allows more sane debugging.
_catch_handlers: Mapping[
    type[BaseException] | Iterable[type[BaseException]],
    Callable[[BaseExceptionGroup[Any]], Any],
] = {
    Exception: _exception_handler,
}


def get_catch_handlers() -> Mapping[
    type[BaseException] | Iterable[type[BaseException]],

View on GitHub (pinned to 1f02114297)

Solutions

  1. Verify the server is running and the URL/host/port are correct.
  2. Increase the client timeout (e.g. `httpx.Timeout(connect=30)`) passed to the Client.
  3. Check network/firewall/VPN and DNS resolution for the host.
  4. Retry with backoff if the server is known to be intermittently slow.

Example fix

// before
async with Client('https://api.example.com/mcp') as client: ...
// after
import httpx
timeout = httpx.Timeout(60.0, connect=30.0)
async with Client('https://api.example.com/mcp', timeout=timeout) as client: ...
Defensive patterns

Strategy: retry

Validate before calling

import socket
socket.create_connection((host, port), timeout=5)  # probe before connecting

Type guard

def is_timeout(err: BaseException) -> bool:
    from fastmcp_exceptions import MCPError  # or check code attribute
    return getattr(err, 'code', None) == 408 or isinstance(getattr(err, 'original', None), Exception)

Try / catch

for attempt in range(3):
    try:
        async with Client(url, timeout=httpx.Timeout(60, connect=30)) as c:
            return await c.list_tools()
    except MCPError as e:
        if e.code != 408:
            raise
        await asyncio.sleep(2 ** attempt)
raise ConnectionError(f'server unreachable: {url}')

Prevention

When it happens

Trigger: Client connecting to an MCP server whose host does not respond within httpx's connect timeout — e.g. `Client('https://slow-host/mcp')` during a request that surfaces the exception group through `_exception_handler`.

Common situations: Wrong host/port, server not started, network latency/firewall dropping SYN packets, DNS resolving but host down, container not listening.

Understand the failure class

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/0699d055470957fb. Report an issue: GitHub.