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
- Verify the server is running and the URL/host/port are correct.
- Increase the client timeout (e.g. `httpx.Timeout(connect=30)`) passed to the Client.
- Check network/firewall/VPN and DNS resolution for the host.
- 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
- Set explicit httpx connect timeouts on clients
- Health-check the MCP server URL before starting workflows
- Confirm server process/port is up in CI before integration tests
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Overall timeout exceeded: {url}
- Timeout fetching {url}
- User server did not start on port {mcp_port}
- Error fetching {url}: {last_error}
- HTTP request timed out ({type(exc).__name__})
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/0699d055470957fb.
Report an issue: GitHub.