PrefectHQ/fastmcp · error · RuntimeError
Server did not start listening on {host}:{port} within {time
Error message
Server did not start listening on {host}:{port} within {timeout} seconds What it means
_wait_for_port repeatedly attempts an asyncio TCP connection to host:port; if the deadline passes while connections are still refused, it raises RuntimeError announcing the server never started listening. The `from None` suppresses the chained ConnectionRefusedError/OSError for a clean message.
Source
Thrown at fastmcp_slim/fastmcp/utilities/tests.py:162
proc.terminate()
proc.join(timeout=5)
if proc.is_alive():
# If it's still alive, then force kill it
proc.kill()
proc.join(timeout=2)
if proc.is_alive():
raise RuntimeError("Server process failed to terminate even after kill")
async def _wait_for_port(host: str, port: int, timeout: float = 5.0) -> None:
"""Poll until a TCP connection to `host:port` is accepted, or raise on timeout."""
deadline = time.monotonic() + timeout
while True:
try:
_, writer = await asyncio.open_connection(host, port)
except (ConnectionRefusedError, OSError):
if time.monotonic() >= deadline:
raise RuntimeError(
f"Server did not start listening on {host}:{port} "
f"within {timeout} seconds"
) from None
await asyncio.sleep(0.001)
else:
writer.close()
with suppress(ConnectionResetError, BrokenPipeError):
await writer.wait_closed()
return
@asynccontextmanager
async def run_server_async(
server: FastMCP,
port: int | None = None,
transport: Literal["http", "streamable-http", "sse"] = "http",
path: str = "/mcp",
host: str = "127.0.0.1",View on GitHub (pinned to 1f02114297)
Solutions
- Confirm the server actually started and binds the expected host/port before/while polling
- Use 127.0.0.1 explicitly instead of localhost to avoid IPv6 mismatch
- Increase the timeout parameter if startup is legitimately slow
- Check server logs for startup exceptions
Example fix
// before
await _wait_for_port("localhost", 8000) # may resolve to ::1
// after
await _wait_for_port("127.0.0.1", 8000, timeout=10.0) Defensive patterns
Strategy: retry
Validate before calling
import socket
try:
socket.create_connection(("127.0.0.1", port), timeout=1).close()
except OSError:
... # port not open yet — start server before waiting Try / catch
try:
await _wait_for_port("127.0.0.1", port, timeout=10.0)
except RuntimeError as e:
print(f"server not up: {e}") # inspect server logs before proceeding Prevention
- Use 127.0.0.1, not localhost, to avoid IPv6 mismatch
- Increase timeout on slow CI
- Confirm the server binds before polling
- Watch server stderr for startup crashes
When it happens
Trigger: Calling run_server_async (or any path using _wait_for_port) against a host:port where no server binds within the timeout (default 5s) — server not started yet, crashed, bound to a different interface, or wrong port passed.
Common situations: Wrong host (localhost resolving to ::1 while server binds 127.0.0.1); server crash during startup; slow CI machines exceeding the 5s window; connecting before the server coroutine actually starts.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- User server did not start on port {mcp_port}
- Overall timeout exceeded: {url}
- Timeout fetching {url}
- HTTP request timed out ({type(exc).__name__})
- Upstream request timed out, please retry
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/f9a1ef1bc87fdaf7.
Report an issue: GitHub.