FastAPI "httpx.ConnectError: Connection refused" in CI
An httpx client tried to reach your app but nothing was listening on that host and port yet. In CI the server either had not finished its startup, crashed on boot, or is bound to a different address than the test uses.
What this error means
A test using httpx against a live server fails with "httpx.ConnectError: [Errno 111] Connection refused" while unit tests pass.
httpx.ConnectError: [Errno 111] Connection refused
The above exception was the direct cause of the following exception:
httpx.ConnectErrorCommon causes
The request raced ahead of server startup
The test fired before uvicorn finished binding, so the connection was refused. CI runners are often slower to boot than a laptop.
The server crashed during startup or bound another address
A lifespan or import error killed the server, or it listens on 127.0.0.1 while the test targets a different host.
How to fix it
Wait for readiness before requesting
- Poll a health endpoint until it responds instead of sleeping a fixed time.
- Confirm the client base URL matches the host and port uvicorn bound.
- Check the server log for a startup exception if it never becomes ready.
import httpx, time
def wait_ready(url, timeout=30):
deadline = time.time() + timeout
while time.time() < deadline:
try:
if httpx.get(url).status_code == 200:
return
except httpx.ConnectError:
time.sleep(0.5)
raise RuntimeError("server never became ready")Drive the app in-process instead
Use httpx.ASGITransport (or TestClient) to call the app object directly, removing the socket and the startup race entirely.
import httpx
from app.main import app
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as ac:
r = await ac.get("/health")How to prevent it
- Poll a readiness endpoint rather than sleeping a fixed interval.
- Prefer in-process ASGI transport for endpoint tests.
- Log and surface startup exceptions so a crashed boot is not mistaken for a race.