FastAPI "RuntimeError: There is no current event loop" in CI
An async test or fixture called asyncio.get_event_loop() when no loop was running. On recent Python versions that no longer auto-creates a loop, and without pytest-asyncio driving the test, the coroutine has nothing to run on.
What this error means
An async FastAPI test fails with "RuntimeError: There is no current event loop in thread 'MainThread'" or a coroutine "was never awaited" warning and no assertions run.
RuntimeError: There is no current event loop in thread 'MainThread'.
loop = asyncio.get_event_loop()Diagnose it: what did pytest actually collect?
Most pytest failures that only happen in CI are collection or import-path problems rather than test failures. The runner has a different working directory, a different sys.path, and usually no editable install, so a test module that imports your package locally may not resolve at all.
# what would run, without running it
pytest --collect-only -q | tail -20
# where pytest thinks the root is (drives conftest and import mode)
pytest --collect-only 2>&1 | grep -i rootdir
# is the package importable at all from the runner cwd?
python -c "import yourpackage, sys; print(yourpackage.__file__)"
python -c "import sys; print(sys.path)"Common causes
pytest-asyncio is not driving the async test
Without pytest-asyncio (and its mode), an async def test is collected but never awaited, so loop-dependent code fails.
get_event_loop with no running loop on newer Python
Recent Python deprecated auto-creating a loop in get_event_loop(), so code that assumed one exists now raises.
How to fix it
Install and enable pytest-asyncio
- Install pytest-asyncio in the test environment.
- Set an asyncio mode so async tests are awaited.
- Use an async client such as httpx.AsyncClient with the app.
import pytest, httpx
from app.main import app
@pytest.mark.asyncio
async def test_health():
transport = httpx.ASGITransport(app=app)
async with httpx.AsyncClient(transport=transport, base_url="http://test") as ac:
r = await ac.get("/health")
assert r.status_code == 200Get the running loop instead of a current one
Inside async code, use asyncio.get_running_loop() rather than get_event_loop().
import asyncio
loop = asyncio.get_running_loop()Make a zero-test run fail the build
# fail explicitly when nothing is collected
pytest --strict-markers -q
test "${PIPESTATUS[0]}" -ne 5 || { echo "pytest collected no tests"; exit 1; }How to prevent it
- Drive async tests with pytest-asyncio and an explicit mode.
- Use
get_running_loop()inside coroutines. - Prefer async clients for async endpoints instead of manual loops.