FastAPI: Causes and Solutions for sqlalchemy.exc.MissingGreenlet Error
When using SQLAlchemy with FastAPI, you may encounter the following error:
sqlalchemy.exc.MissingGreenlet: greenlet_spawn has not been called; can't call await_only() here. Was IO attempted in an unexpected place?
This error occurs when asynchronous (async) and synchronous (sync) operations are not handled correctly.
In this article, we’ll explore possible causes and solutions with before-and-after code examples.
Causes and Solutions
Cause 1: Using a Synchronous Engine (create_engine()) Inside an Asynchronous Function (async def)
To handle asynchronous processing properly in FastAPI, you must use an asynchronous engine (create_async_engine()).
If you use a synchronous engine (create_engine()), SQLAlchemy’s sync methods won’t work within an async function, leading to the MissingGreenlet error.
Before Fix
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# Using a synchronous engine
SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
async def get_db():
db = SessionLocal()
try:
yield db # Using a sync engine inside an async function
finally:
db.close()
After Fix
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
# Using an asynchronous engine
SQLALCHEMY_DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"
engine = create_async_engine(SQLALCHEMY_DATABASE_URL, echo=True)
AsyncSessionLocal = sessionmaker(bind=engine, class_=AsyncSession, expire_on_commit=False)
async def get_db():
async with AsyncSessionLocal() as db:
yield db # Using an async engine inside an async function
Cause 2: Using a Synchronous Session (SessionLocal) Inside an Asynchronous Function (async def)
Inside async def functions, you cannot directly call .commit() or .execute() on a synchronous SessionLocal.
SQLAlchemy 2.0 requires using AsyncSession for async operations.
Before Fix
async def create_user(db, user_data):
new_user = User(**user_data)
db.add(new_user)
db.commit() # Error occurs here
db.refresh(new_user)
return new_user
After Fix
async def create_user(db: AsyncSession, user_data):
new_user = User(**user_data)
db.add(new_user)
await db.commit() # Use await for async operations
await db.refresh(new_user)
return new_user
Cause 3: Using Depends(get_db) with a Synchronous Session Inside an Asynchronous Function (async def)
In FastAPI, database sessions are often passed using Depends(get_db),
but using a synchronous session (Session) inside an async def function will trigger the MissingGreenlet error.
Before Fix
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
app = FastAPI()
async def get_users(db: Session = Depends(get_db)): # `Session` is for sync operations
return db.query(User).all() # Error occurs
After Fix
from sqlalchemy.ext.asyncio import AsyncSession
async def get_users(db: AsyncSession = Depends(get_db)):
result = await db.execute(select(User)) # Use async query execution
return result.scalars().all()
Cause 4: Defining FastAPI Route Functions (@app.get) as Synchronous (def) While Using await Inside
In FastAPI, using def where async def is required will prevent internal await calls from executing properly, causing an error.
Before Fix
@app.get("/users")
def get_users(db: AsyncSession = Depends(get_db)): # `def` instead of `async def`
result = await db.execute(select(User)) # Error occurs here
return result.scalars().all()
After Fix
@app.get("/users")
async def get_users(db: AsyncSession = Depends(get_db)): # Use `async def`
result = await db.execute(select(User))
return result.scalars().all()
Cause 5: Not Using asyncpg Driver
When using an async engine, you must install and use asyncpg as the PostgreSQL driver.
Before Fix
# Using `postgresql://` will enforce synchronous connections
SQLALCHEMY_DATABASE_URL = "postgresql://user:password@localhost/dbname"
After Fix
# Use `postgresql+asyncpg://` for async connections
SQLALCHEMY_DATABASE_URL = "postgresql+asyncpg://user:password@localhost/dbname"
Install the required package:
pip install asyncpg
Summary
| Cause | Before Fix | After Fix |
|---|---|---|
| Using a sync engine in an async function | create_engine() |
create_async_engine() |
| Using a sync session in an async function | db.commit() |
await db.commit() |
Using Depends(get_db) with a sync session |
Session = Depends(get_db) |
AsyncSession = Depends(get_db) |
FastAPI route function is sync (def) |
def get_users() |
async def get_users() |
Not using asyncpg |
postgresql:// |
postgresql+asyncpg:// |
Conclusion
This error occurs when SQLAlchemy’s async features are not used correctly.
To resolve it, ensure you use the async engine (create_async_engine), handle sessions with AsyncSession,
and remember to await async queries.
By following these best practices, you can correctly integrate SQLAlchemy with FastAPI and avoid MissingGreenlet errors!
