---
title: "FastAPI Response Model Silently Strips Fields Not in Schema"
aliases: [fastapi-field-strip, pydantic-response-model-missing-field, fastapi-silent-omit]
tags: [fastapi, pydantic, api, gotcha, debugging]
sources:
  - "daily/2026-05-01.md"
created: 2026-05-01
updated: 2026-05-01
---

# FastAPI Response Model Silently Strips Fields Not in Schema

When a FastAPI route has a `response_model`, Pydantic serializes the return value using **only** the fields declared in that model. Any extra fields — even if they exist in the MongoDB document and are present in the Python object — are silently dropped from the JSON response. No warning is raised, no error is logged.

## Key Points

- FastAPI's `response_model` is a whitelist: fields not declared are silently excluded from the response
- The data exists in the database and in the Python layer — it never reaches the HTTP response
- Frontend receives `undefined` for the missing field and typically falls back to a default/null state, masking the bug
- **Diagnostic first step**: when API data appears "missing", check the Pydantic response model schema before investigating the database or service layer
- Fix: add the missing field to the response model, or use `response_model=None` to disable filtering (not recommended in production)
- `response_model_exclude_none=True` does not cause this — that only removes `None` values that ARE in the schema

## Details

### Minimal Reproduction

```python
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class TaskResponse(BaseModel):
    id: str
    title: str
    # ← 'failure' and 'error' fields are NOT declared here

@app.get("/tasks/{task_id}", response_model=TaskResponse)
async def get_task(task_id: str):
    # DB returns: {"id": "abc", "title": "Export", "failure": "timeout", "error": "Connection refused"}
    doc = await db.tasks.find_one({"_id": task_id})
    return doc
# Response JSON: {"id": "abc", "title": "Export"}
# 'failure' and 'error' are silently stripped
```

### The Fix

```python
class TaskResponse(BaseModel):
    id: str
    title: str
    failure: str | None = None   # ← add missing fields
    error: str | None = None
```

### Debugging Workflow

```
Bug: frontend shows no error message for failed tasks
  ↓
1. Check Pydantic response model — is 'failure' field declared?  ← START HERE
   → No → add it → bug fixed
2. If field IS in model, check service layer returns it
3. If service returns it, check MongoDB document structure
4. If MongoDB has it, check field name spelling (camelCase vs snake_case)
```

> [!warning] Silent Failure Pattern
> This is one of the most deceptive FastAPI bugs: the data pipeline is fully correct, but the response contract silently discards fields. Unlike missing DB documents (which raise errors), this produces valid 200 responses with incomplete data.

### When to Use `response_model=None`

```python
# Only for debugging or internal endpoints — disables all output validation
@app.get("/debug/tasks/{task_id}", response_model=None)
async def debug_task(task_id: str):
    return await db.tasks.find_one({"_id": task_id})
```

## Vue Template Null Crash (List Fields)

When the missing field is a **list** (e.g. `tags: list[str]`), Vue templates crash with a `TypeError` rather than silently showing nothing:

```
TypeError: Cannot read properties of null (reading 'length')
  at task.tags.length
```

This is because Vue evaluates `task.tags.length` eagerly. The fix requires **both** layers:

1. **Backend** — add the field to the Pydantic response model with a default: `tags: list[str] = []`
2. **Frontend** — add optional chaining in the template as a defensive guard: `task.tags?.length`

Fixing only the backend prevents new crashes; optional chaining protects against cached responses or race conditions.

> [!warning] Dual-layer fix
> Schema fix alone is insufficient during rollout — old cached API responses or in-flight requests may still deliver null. Always add `?.` optional chaining when accessing list/object properties from API responses.

## Related Concepts

- [[wiki/concepts/fastapi-orm-property-json-column]] — when the missing field's data already lives in a JSON column, expose it via ORM `@property` without a migration
- [[wiki/concepts/pydantic-v2-alias-id-gotcha]] — another Pydantic field serialization gotcha where field names differ from JSON keys
- [[wiki/concepts/pydantic-model-dict-interface]] — Pydantic model/dict boundary issues that cause silent data loss
- [[wiki/tech-patterns/fastapi-python-docker]] — FastAPI deployment context where these issues surface

## Sources

- [[daily/2026-05-01.md]] — Session 17:13: frontend showed `undefined` for task failure reason; root cause traced to `failure` and `error` fields missing from the Pydantic `TaskResponse` model
- [[daily/2026-05-07.md]] — Session 12:09: Vue template crashed with `TypeError: Cannot read properties of null (reading 'length')` on `task.tags.length` — `tags` was absent from FastAPI schema; dual-layer fix required
