Quick reference for the recurring FastAPI errors you’ll see and the fix.

422 on what looks like valid input

Cause: parameter declared without Body() is treated as query param.

# Wrong
@app.post("/x")
async def x(name: str): ...   # name is QUERY (not body)

# Right
@app.post("/x")
async def x(name: str = Body(...)): ...
# Or use a Pydantic model.

“RuntimeError: dictionary changed size during iteration” in tests

Cause: not clearing dependency_overrides between tests.

@pytest.fixture
def client():
    app.dependency_overrides[get_db] = fake_db
    yield TestClient(app)
    app.dependency_overrides.clear()    # always

CORS not working / preflight 400

Cause: CORS middleware order, or allow_credentials=True with allow_origins=["*"].

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://yourdomain.com"],   # not ["*"] when credentials=True
    allow_credentials=True,
    allow_methods=["*"], allow_headers=["*"],
)

CORS must be added LAST among middleware so it runs FIRST.

TypeError: object dict can’t be used in ‘await’

Cause: missing async somewhere.

# Wrong
def get_db(): return AsyncSession(...)

@app.get("/")
async def home(db = Depends(get_db)):
    await db.execute(...)             # error: AsyncSession not awaited

Use a yield-style async dep:

async def get_db():
    async with AsyncSessionLocal() as session:
        yield session

“MissingGreenlet” / SQLAlchemy async errors

Cause: lazy-loading a relationship in async context.

# Wrong
user = await session.scalar(select(User).where(...))
print(user.posts)            # implicit lazy load -> error

# Right
stmt = select(User).options(selectinload(User.posts)).where(...)
user = await session.scalar(stmt)

Or use await user.awaitable_attrs.posts.

“InvalidRequestError: This Session was already attached to a transaction”

Cause: nested session.begin() without begin_nested.

async with session.begin():
    async with session.begin_nested():    # savepoint
        ...

Or use a single transaction.

Body always None

Cause: missing python-multipart for forms, missing Content-Type header.

uv add python-multipart

Client must send Content-Type: application/x-www-form-urlencoded (or multipart/form-data for files).

Pydantic v1 vs v2 confusion

# v1 (legacy)
.dict(), .json(), .parse_obj(...)

# v2 (current)
.model_dump(), .model_dump_json(), .model_validate(...)

If using pydantic v1: upgrade. v1 is in maintenance.

OpenAPI shows wrong response shape

Cause: missing response_model.

@app.get("/users/{id}", response_model=UserOut)   # ALWAYS set this

response_model_exclude not respected

Cause: returning JSONResponse(...) directly bypasses response_model.

# Wrong
return JSONResponse(user.dict())

# Right
return user                              # FastAPI applies response_model

“Form data requires python-multipart”

uv add python-multipart

Long-running endpoint blocking other requests

Cause: synchronous code in async handler.

# Wrong
@app.get("/slow")
async def slow():
    time.sleep(5)             # blocks event loop

# Right
async def slow():
    await asyncio.sleep(5)
# Or
async def slow():
    await asyncio.to_thread(legacy_blocking_fn)

Stuck connections after prod restart

Cause: missing pool_pre_ping.

engine = create_async_engine(URL, pool_pre_ping=True, pool_recycle=300)

“Multiple heads detected” in Alembic

Cause: parallel migrations.

alembic heads             # lists heads
alembic merge -m "merge X+Y" <head1> <head2>

See Alembic Textbook Ch 4 .

Pydantic alias not accepted on input

class M(BaseModel):
    full_name: str = Field(alias="fullName")
    model_config = {"populate_by_name": True}    # accept BOTH

OAuth2 token endpoint expects form data

/token must accept application/x-www-form-urlencoded. The Swagger Authorize button sends form-encoded; if your endpoint takes JSON it won’t fit OAuth2 standards.

ImportError: cannot import name ‘AsyncSession’

from sqlalchemy.ext.asyncio import AsyncSession      # not from sqlalchemy

MissingError on session.begin_nested() in async

async with session.begin():
    async with session.begin_nested() as sp:
        ...

begin_nested() requires an outer transaction.

502 / 504 in production

Common causes:

  • Worker died (OOM): check memory limits.
  • Long request exceeded LB timeout: tune.
  • Lifespan hung: don’t await indefinitely in startup.

Read this next

If you want my FastAPI debug runbook, it’s at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .