Skip to content
Latchkey

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.

python
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.

Terminal
# 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

  1. Install pytest-asyncio in the test environment.
  2. Set an asyncio mode so async tests are awaited.
  3. Use an async client such as httpx.AsyncClient with the app.
tests/test_api.py
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 == 200

Get the running loop instead of a current one

Inside async code, use asyncio.get_running_loop() rather than get_event_loop().

app/tasks.py
import asyncio
loop = asyncio.get_running_loop()

Make a zero-test run fail the build

Terminal
# 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.

Frequently asked questions

What causes FastAPI "RuntimeError: there is no current event loop" in CI?
There are 2 common causes: pytest-asyncio is not driving the async test and get_event_loop with no running loop on newer python. Without pytest-asyncio (and its mode), an async def test is collected but never awaited, so loop-dependent code fails.
How do I fix FastAPI "RuntimeError: there is no current event loop" in CI?
There are 2 fixes depending on which cause you have: install and enable pytest-asyncio and get the running loop instead of a current one. Work through them in order, since the first is the most common.
What does FastAPI "RuntimeError: there is no current event loop" in CI actually mean?
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.
How do I stop FastAPI "RuntimeError: there is no current event loop" in CI happening again?
Drive async tests with pytest-asyncio and an explicit mode. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card