Fix ValidationError: pydantic.error_wrappers.ValidationError: 1 validation error for UserResponse - field required (type=value_error.missing) in FastAPI
This error occurs when the data returned by your endpoint does not match the response_model schema. FastAPI validates the response through Pydantic, and missing or mistyped fields trigger a ValidationError. Fix it by ensuring the returned data includes all required fields defined in the response model or by making optional fields truly Optional.
Reading the Stack Trace
Here's what each line means:
- File "/app/venv/lib/python3.11/site-packages/fastapi/routing.py", line 199, in serialize_response: FastAPI serializes the response through the response_model and Pydantic catches that the 'email' field is missing.
- File "/app/venv/lib/python3.11/site-packages/pydantic/fields.py", line 884, in validate: Pydantic validates the response data against the schema and raises because a required field is not present.
- email field required (type=value_error.missing): The 'email' field is required in the UserResponse model but the endpoint returned data without it.
Common Causes
1. Database model missing a field the response model requires
The ORM model does not have a column that the Pydantic response model expects, so the serialized response is missing the field.
class UserResponse(BaseModel):
id: int
name: str
email: str # Required field
@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
return user # User ORM model may not have 'email' populated
2. Returning a dict with missing keys
The endpoint returns a dictionary that is missing one or more required keys defined in the response model.
@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int):
return {"id": user_id, "name": "Alice"} # Missing 'email' key
3. Field name mismatch between ORM and Pydantic models
The ORM model uses a different column name than the Pydantic model expects, so the field is not mapped.
# ORM model has 'email_address' column
# Pydantic model expects 'email' field
class UserResponse(BaseModel):
id: int
name: str
email: str # Does not match ORM column 'email_address'
The Fix
Make the email field Optional with a default of None if it may not always be present. Add model_config with from_attributes=True (Pydantic v2) so the model can read attributes directly from ORM objects. Add a 404 check to handle missing users.
from pydantic import BaseModel
class UserResponse(BaseModel):
id: int
name: str
email: str
@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
return user
from pydantic import BaseModel, ConfigDict
from typing import Optional
class UserResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
email: Optional[str] = None
@app.get("/users/{user_id}", response_model=UserResponse)
def get_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
Testing the Fix
import pytest
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_get_user_with_all_fields():
response = client.get("/users/1")
if response.status_code == 200:
data = response.json()
assert "id" in data
assert "name" in data
assert "email" in data
def test_get_user_without_email():
# User exists but has no email
response = client.get("/users/2")
if response.status_code == 200:
data = response.json()
assert data["email"] is None
def test_get_nonexistent_user_returns_404():
response = client.get("/users/99999")
assert response.status_code == 404
def test_response_excludes_sensitive_fields():
response = client.get("/users/1")
if response.status_code == 200:
data = response.json()
assert "password" not in data
assert "hashed_password" not in data
Run your tests:
pytest tests/test_response_model.py -v
Pushing Through CI/CD
git checkout -b fix/fastapi-response-model,git add src/schemas/user.py src/routes/users.py tests/test_response_model.py,git commit -m "fix: make email optional in UserResponse and add from_attributes config",git push origin fix/fastapi-response-model
Your CI config should look something like this:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: testdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- run: pip install -r requirements.txt
- run: pytest --tb=short -q
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
The Full Manual Process: 18 Steps
Here's every step you just went through to fix this one bug:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- Monitor production to confirm the error is resolved
Total time: 30-60 minutes. For one bug.
Or Let bugstack Fix It in Under 2 minutes
Every step above? bugstack does it automatically.
Step 1: Install the SDK
pip install bugstack
Step 2: Initialize
import bugstack
bugstack.init(api_key=os.environ["BUGSTACK_API_KEY"])
Step 3: There is no step 3.
bugstack handles everything from here:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- Deploys to production (or opens a PR for review)
Time from error to fix deployed: Under 2 minutes.
Human involvement: zero.
Try bugstack Free →No credit card. 5-minute setup. Cancel anytime.
Deploying the Fix (Manual Path)
- Run the test suite locally to confirm response serialization works for all user states.
- Open a pull request with the schema and endpoint fixes.
- Wait for CI checks to pass on the PR.
- Have a teammate review and approve the PR.
- Merge to main and verify API responses match the expected schema in staging.
Frequently Asked Questions
BugStack validates that every endpoint's returned data passes the response_model validation, tests edge cases like missing optional fields, and confirms no sensitive fields leak through the schema.
BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review the schema changes before merging.
response_model actively filters and validates the response data through Pydantic, stripping extra fields. Return type hints are only for documentation and IDE support, they do not perform runtime validation.
In Pydantic v2, use model_config = ConfigDict(from_attributes=True). In Pydantic v1, use class Config: orm_mode = True. Both allow Pydantic to read data from ORM model attributes.