Pydantic Field required [type=missing] (tested)

Pydantic raises Field required [type=missing] when a field with no default is absent from the input. In v2 the usual causes are an Optional[...] field without = None, a field alias you did not send, or (in FastAPI) a parameter it expected in the query rather than the body.

1. Optional[str] is still required in Pydantic v2

from typing import Optional
from pydantic import BaseModel, ValidationError
class User(BaseModel):
    name: str
    nickname: Optional[str]          # no default -> still required in v2
    email: str | None = None         # default -> optional
for data in ({"name": "Ann"}, {"name": "Ann", "nickname": None}):
    try:
        print("ok:", repr(User(**data)))
    except ValidationError as e:
        print(e)

Output:

1 validation error for User
nickname
  Field required [type=missing, input_value={'name': 'Ann'}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.14/v/missing
ok: User(name='Ann', nickname=None, email=None)

This is the most common surprise after upgrading from Pydantic v1. In v2, Optional[str] only means "may be None"; it does not give the field a default. A field with no default is required, so leaving the key out raises Field required [type=missing], while sending "nickname": null passes. To make a field truly optional, give it a default: nickname: str | None = None.

2. The key matches the field name, but the field has an alias

from pydantic import BaseModel, Field, ValidationError
class Item(BaseModel):
    item_id: int = Field(alias="itemId")
for data in ({"item_id": 1}, {"itemId": 1}):
    try:
        print("ok:", repr(Item(**data)))
    except ValidationError as e:
        print(e)
class Item2(BaseModel):
    model_config = {"populate_by_name": True}
    item_id: int = Field(alias="itemId")
print("populate_by_name:", repr(Item2(item_id=1)))

Output:

1 validation error for Item
itemId
  Field required [type=missing, input_value={'item_id': 1}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.14/v/missing
ok: Item(item_id=1)
populate_by_name: Item2(item_id=1)

When a field has an alias, Pydantic validates against the alias by default, so {"item_id": 1} reports itemId as missing, even though your code calls it item_id. The error's location names the alias, which is the clue. Send the alias, or set populate_by_name so both names work.

3. Find which nested field is missing

from pydantic import BaseModel, ValidationError
class Address(BaseModel):
    city: str
class Order(BaseModel):
    id: int
    ship_to: Address
try:
    Order.model_validate({"id": 7, "ship_to": {}})
except ValidationError as e:
    print(e)
    print(e.errors(include_url=False))

Output:

1 validation error for Order
ship_to.city
  Field required [type=missing, input_value={}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.14/v/missing
[{'type': 'missing', 'loc': ('ship_to', 'city'), 'msg': 'Field required', 'input': {}}]

For nested models the location is a dotted path (ship_to.city). In code, e.errors() gives the same thing as a tuple in loc, which is easier to log or return to a client than the printed message.

4. "Field required" in a FastAPI 422 response

import warnings; warnings.simplefilter("ignore")
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
    name: str
    price: float
@app.post("/items")
def create(item: Item):
    return item
@app.get("/search")
def search(q: str):
    return {"q": q}
c = TestClient(app)
print(c.post("/items", json={"name": "pen"}).json())
print(c.get("/search").json())
print(c.post("/items", data={"name": "pen", "price": "1"}).json())

Output:

{'detail': [{'type': 'missing', 'loc': ['body', 'price'], 'msg': 'Field required', 'input': {'name': 'pen'}}]}
{'detail': [{'type': 'missing', 'loc': ['query', 'q'], 'msg': 'Field required', 'input': None}]}
{'detail': [{'type': 'model_attributes_type', 'loc': ['body'], 'msg': 'Input should be a valid dictionary or object to extract fields from', 'input': 'name=pen&price=1'}]}

FastAPI returns Pydantic's errors as a 422 response. The first element of loc tells you where FastAPI looked: body (a JSON field is missing), query (a function parameter without a default became a required query parameter), path or header. The third call shows a different error: form data sent to a JSON-body route fails as a whole, not field by field. More causes in FastAPI 422 Unprocessable Entity.

Checklist

Related: Pydantic "Extra inputs are not permitted", BaseSettings has been moved to pydantic-settings.

Frequently asked questions

Why is my Optional field required in Pydantic v2?

In Pydantic v2 Optional[str] only allows None; without a default the field is still required. Write x: str | None = None.

Why does Pydantic say a field is missing when I sent it?

The field probably has an alias, and Pydantic validates by alias. The error location shows the alias; send that key or set populate_by_name=True.

How do I see which field is missing in FastAPI?

The 422 response detail has loc, e.g. ['body', 'price'] or ['query', 'q']; the first element says where FastAPI looked.

Written with AI assistance. Measured on 2026-10-23 with Pydantic 2.14.1, FastAPI 0.161.1 and Python 3.14.7 (python:3-alpine in Docker). Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python and Pydantic are free.