Skip to content
Latchkey

FastAPI "greenlet_spawn has not been called" (async SQLAlchemy) in CI

Async SQLAlchemy runs blocking DBAPI work inside a greenlet. When IO is triggered outside that context, usually a lazy-loaded relationship accessed after the session closed, it raises MissingGreenlet. Tests surface this when serializing ORM objects to a response model.

What this error means

A test fails with "sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here" while building a response from an ORM object.

python
sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call
await_only() here. Was IO attempted in an unexpected place?

Common causes

Lazy loading a relationship outside the session

A response model reads a related attribute that was not loaded; the lazy load triggers IO after the async context ended.

greenlet is not installed for the async engine

The async engine needs the greenlet package; a slim install without it cannot run the async DBAPI bridge.

How to fix it

Eager-load relationships you serialize

Load related data inside the async query so no lazy IO happens during serialization.

app/crud.py
from sqlalchemy.orm import selectinload
from sqlalchemy import select

stmt = select(Order).options(selectinload(Order.items))
orders = (await session.execute(stmt)).scalars().all()

Ensure greenlet is installed

Install greenlet so the async engine has its execution bridge (SQLAlchemy async extras pull it in).

Terminal
python -m pip install "sqlalchemy[asyncio]" greenlet

How to prevent it

  • Eager-load with selectinload/joinedload for data you serialize.
  • Install sqlalchemy[asyncio] so greenlet is present.
  • Keep ORM access inside the async session scope.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →