Common Import and Type Errors
Most FastAPI errors at startup or during request handling come from Pydantic validation mismatches, circular imports, or missing packages. This lesson covers root-cause diagnosis — not just the error message, but why it happens.
By the end of this lesson you can: read FastAPI and Pydantic error tracebacks, identify the exact field or import causing the issue, and apply the correct fix.
ImportError: cannot import name X from fastapi
Cause: The symbol moved, was renamed, or never existed in the version you installed.
Diagnose:
python -c "import fastapi; print(fastapi.__version__)"
pip show fastapi | grep Version
Common examples:
| Error | Cause | Fix |
|---|---|---|
cannot import name 'Annotated' from 'fastapi' | Using Python 3.8 without typing backport | from typing import Annotated (Python 3.9+) or from typing_extensions import Annotated |
cannot import name 'pydantic_settings' | Separate package since Pydantic v2 | pip install pydantic-settings |
cannot import name 'validator' from 'pydantic' | Pydantic v1 API in v2 | Replace @validator with @field_validator |
422 Unprocessable Entity — Reading the Detail
{
"detail": [
{
"type": "missing",
"loc": ["body", "email"],
"msg": "Field required",
"input": {"username": "alice"}
}
]
}
locshows the path:["body", "email"]means theemailfield in the request body.typeis the Pydantic error code:missing,string_too_short,int_parsing, etc.inputis what was actually received.
Diagnose:
curl -s -X POST http://localhost:8000/users/ \
-H "Content-Type: application/json" \
-d '{"username": "alice"}' | python -m json.tool
Pydantic v1 → v2 Migration Errors
| v1 Code | Error in v2 | v2 Replacement |
|---|---|---|
class Config: orm_mode = True | PydanticUserError | model_config = ConfigDict(from_attributes=True) |
@validator("field") | PydanticUserError | @field_validator("field") |
model.dict() | AttributeWarning | model.model_dump() |
model.json() | Deprecated | model.model_dump_json() |
schema_extra | Ignored | json_schema_extra in model_config |
Circular Import Errors
Symptom: ImportError: cannot import name 'X' from partially initialized module 'app.Y'
Root cause: app.routers.users imports from app.services.users, which imports from app.db.models.user, which imports from app.routers.users.
Fix: Break the cycle with TYPE_CHECKING:
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from app.repositories.user_repo import UserRepository # Only for type hints
Or restructure: models → repositories → services → routers (one direction only).
AttributeError: 'coroutine' object has no attribute 'X'
Cause: Called an async function without await.
# ❌ Wrong
user = get_user(db, user_id) # Returns a coroutine, not a User
print(user.username) # AttributeError
# ✅ Correct
user = await get_user(db, user_id)
print(user.username)
RuntimeError: no running event loop
Cause: Calling asyncio.get_event_loop().run_until_complete() inside an async context, or running async code at module import time.
Fix: Never call sync asyncio.run() inside async def. Use await directly.
TypeError: Object of type X is not JSON serializable
Cause: Returning a non-serializable object from a route.
# ❌ Returns datetime object directly
return {"created_at": user.created_at} # datetime not serializable
# ✅ Use Pydantic model — it handles serialization
return UserResponse.model_validate(user)
Or set json_encoders in model config:
model_config = ConfigDict(json_encoders={datetime: lambda v: v.isoformat()})
Common Pitfalls Summary
| Error | Root Cause | Quick Fix |
|---|---|---|
422 on every POST | Wrong Content-Type header | Add -H "Content-Type: application/json" |
404 for valid route | Router not included | Call app.include_router(router) |
500 with no detail | Unhandled exception | Add global exception handler + logging |
| Model validation ignores extra fields | Default Pydantic v2 behavior | Set extra="forbid" to catch unexpected fields |
DetachedInstanceError | Accessing ORM object after session close | Set expire_on_commit=False |