Fix: Pydantic ValidationError — Field Required / Value Not Valid
Quick Answer
How to fix Pydantic ValidationError in Python — missing required fields, wrong types, custom validators, handling optional fields, v1 vs v2 API differences, and debugging complex nested models.
The Error
A Pydantic model raises a ValidationError when you try to create or parse data:
from pydantic import BaseModel
class User(BaseModel):
id: int
email: str
age: int
User(id="not-an-int", email="[email protected]")pydantic_core._pydantic_core.ValidationError: 2 validation errors for User
id
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='not-an-int', input_url=...]
age
Field required [type=missing, input_value={'id': 'not-an-int', 'email': '[email protected]'}, input_url=...]Or when parsing JSON in FastAPI:
422 Unprocessable Entity
{
"detail": [
{"type": "missing", "loc": ["body", "age"], "msg": "Field required"},
{"type": "int_parsing", "loc": ["body", "id"], "msg": "Input should be a valid integer"}
]
}Why This Happens
Pydantic validates data types and constraints strictly at model instantiation time:
- Missing required field — a field with no default value was not provided in the input.
- Wrong type that can’t be coerced —
id: intreceived"not-an-int". Pydantic tries to coerce compatible types ("42"→42) but fails for incompatible ones. - Failed custom validator — a
@field_validatoror@validatorraised an error. - Constraint violation —
Field(gt=0)received a negative number, ormax_length=100was exceeded. - Nested model validation failure — a field typed as another Pydantic model received incompatible data.
- Pydantic v1 vs v2 API mismatch — Pydantic v2 changed many APIs. Code written for v1 fails with v2 import paths or different validator syntax.
Fix 1: Provide All Required Fields
A field without a default value is required. The ValidationError message shows Field required for every missing field:
from pydantic import BaseModel
from typing import Optional
class User(BaseModel):
id: int # Required — no default
email: str # Required
age: int # Required
bio: Optional[str] = None # Optional — defaults to None
role: str = "user" # Optional — has a default
# Missing age → ValidationError
User(id=1, email="[email protected]")
# Correct
User(id=1, email="[email protected]", age=30)
# bio and role have defaults — can be omitted
User(id=1, email="[email protected]", age=30) # role="user", bio=NoneCheck which fields have defaults by inspecting the model:
for name, field in User.model_fields.items():
required = field.is_required()
print(f"{name}: required={required}, default={field.default}")Fix 2: Fix Type Mismatches
Pydantic coerces compatible types but rejects incompatible ones. Understanding what Pydantic will and won’t coerce:
class Item(BaseModel):
price: float
count: int
active: bool
# Coerced successfully
Item(price="9.99", count="5", active=1)
# price=9.99 (str→float), count=5 (str→int), active=True (int→bool)
# Fails — "yes" can't be parsed as bool or int
Item(price=9.99, count=5, active="yes")
# ValidationError: Input should be a valid boolean [type=bool_parsing]Use model_validate for dict input instead of unpacking:
data = {"price": "9.99", "count": "5", "active": True}
# Correct
item = Item.model_validate(data)
# Also correct but less explicit
item = Item(**data)Use model_validate_json for JSON strings:
import json
json_str = '{"price": 9.99, "count": 5, "active": true}'
item = Item.model_validate_json(json_str)Fix 3: Handle Optional and Nullable Fields Correctly
The difference between optional (has a default) and nullable (can be None) matters:
from pydantic import BaseModel
from typing import Optional
class User(BaseModel):
name: str # Required, cannot be None
bio: Optional[str] = None # Optional AND nullable — can be omitted or None
nickname: str | None = None # Same as above (Python 3.10+ syntax)
age: Optional[int] # Nullable but REQUIRED — must be provided, even as None# Works — age is provided as None
User(name="Alice", age=None)
# Fails — age is required (even though it can be None)
User(name="Alice")
# ValidationError: Field required [type=missing] for ageIf you want a field to be truly optional (can be omitted entirely), give it a default:
class User(BaseModel):
name: str
age: Optional[int] = None # Can be omitted, defaults to NoneFix 4: Add Custom Validators
Use @field_validator (Pydantic v2) to add custom validation logic:
from pydantic import BaseModel, field_validator, ValidationInfo
class User(BaseModel):
email: str
age: int
password: str
password_confirm: str
@field_validator('email')
@classmethod
def email_must_contain_at(cls, v: str) -> str:
if '@' not in v:
raise ValueError('must be a valid email address')
return v.lower() # Return the transformed value
@field_validator('age')
@classmethod
def age_must_be_adult(cls, v: int) -> int:
if v < 18:
raise ValueError('must be 18 or older')
return vCross-field validation with @model_validator:
from pydantic import BaseModel, model_validator
class PasswordChange(BaseModel):
password: str
password_confirm: str
@model_validator(mode='after')
def passwords_match(self) -> 'PasswordChange':
if self.password != self.password_confirm:
raise ValueError('passwords do not match')
return selfFix 5: Use Field Constraints Instead of Custom Validators
For common constraints, use Field() instead of writing custom validators:
from pydantic import BaseModel, Field
from typing import Annotated
class Product(BaseModel):
name: str = Field(min_length=1, max_length=100)
price: float = Field(gt=0, le=10000)
quantity: int = Field(ge=0, default=0)
sku: str = Field(pattern=r'^[A-Z]{3}-\d{4}$')
tags: list[str] = Field(default_factory=list, max_length=10)
# Or with Annotated (Pydantic v2 preferred style)
PositivePrice = Annotated[float, Field(gt=0)]
class Product(BaseModel):
price: PositivePriceCommon Field constraints:
| Constraint | Applies to | Meaning |
|---|---|---|
gt | numbers | greater than |
ge | numbers | greater than or equal |
lt | numbers | less than |
le | numbers | less than or equal |
min_length | str, list | minimum length |
max_length | str, list | maximum length |
pattern | str | must match regex |
default_factory | any | callable for mutable defaults |
Fix 6: Fix Pydantic v1 vs v2 API Differences
Pydantic v2 (released 2023) changed many APIs. If you’re migrating or using libraries that bundle Pydantic v1:
| Feature | Pydantic v1 | Pydantic v2 |
|---|---|---|
| Validator decorator | @validator('field') | @field_validator('field') |
| Root validator | @root_validator | @model_validator(mode='before'/'after') |
| Dict output | .dict() | .model_dump() |
| JSON output | .json() | .model_dump_json() |
| Parse dict | Model(**data) or .parse_obj(data) | Model.model_validate(data) |
| Parse JSON | .parse_raw(json_str) | Model.model_validate_json(json_str) |
| Config class | class Config: | model_config = ConfigDict(...) |
Check your Pydantic version:
pip show pydanticUse the v1 compatibility layer in v2 if you have a large migration ahead:
# Force Pydantic v2 to run in v1 compatibility mode for a model
from pydantic.v1 import BaseModel # Uses v1 API even on v2 installOr install the old version:
pip install "pydantic<2"Fix 7: Debug Complex Validation Errors
For nested models or complex validation errors, the error output gives the full location path:
from pydantic import BaseModel
from typing import list
class Address(BaseModel):
street: str
city: str
zip_code: str
class Order(BaseModel):
user_id: int
items: list[str]
shipping_address: Address
try:
Order(
user_id=1,
items=["item1"],
shipping_address={"street": "123 Main St"} # Missing city and zip_code
)
except ValidationError as e:
print(e.error_count()) # 2
for error in e.errors():
print(error['loc']) # ('shipping_address', 'city'), ('shipping_address', 'zip_code')
print(error['msg']) # "Field required"
print(error['type']) # "missing"Programmatically handle specific errors:
from pydantic import ValidationError
def create_user_safe(data: dict) -> User | None:
try:
return User.model_validate(data)
except ValidationError as e:
errors = {'.'.join(str(loc) for loc in err['loc']): err['msg']
for err in e.errors()}
# {'email': 'value is not a valid email address', 'age': 'Field required'}
raise ValueError(f"Invalid user data: {errors}") from eIn FastAPI, customize the 422 error response:
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from pydantic import ValidationError
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=422,
content={
"error": "Validation failed",
"details": [
{
"field": ".".join(str(loc) for loc in err["loc"][1:]),
"message": err["msg"],
}
for err in exc.errors()
],
},
)Still Not Working?
Check if you’re using model_config correctly in v2. The old class Config approach still works but is deprecated:
# Pydantic v2 — preferred
from pydantic import BaseModel, ConfigDict
class User(BaseModel):
model_config = ConfigDict(
str_strip_whitespace=True,
validate_default=True,
arbitrary_types_allowed=True,
)
# Pydantic v1 style (still works in v2 but triggers deprecation warning)
class User(BaseModel):
class Config:
anystr_strip_whitespace = TrueEnable strict mode to prevent any type coercion and get explicit errors:
class StrictUser(BaseModel):
model_config = ConfigDict(strict=True)
id: int
name: str
StrictUser(id="42", name="Alice")
# ValidationError: Input should be a valid integer [type=int_type] — no coercionUse model_construct() to skip validation when you’re certain the data is already valid (e.g., data from your own database):
# Skips validation — use only with trusted data
user = User.model_construct(id=1, email="[email protected]", age=30)For related Python and FastAPI issues, see Fix: FastAPI 422 Unprocessable Entity and Fix: Python TypeError — Missing Required Argument.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Pydantic ValidationError — Field Required, Value Not a Valid Type, or Extra Fields
How to fix Pydantic v2 validation errors — required fields, type coercion, model_validator, custom validators, extra fields config, and migrating from Pydantic v1.
Fix: FastAPI BackgroundTasks Not Working — Task Not Running or Dependency Errors
How to fix FastAPI BackgroundTasks — task not executing, dependency injection in tasks, error handling, Celery for heavy tasks, and lifespan-managed background workers.
Fix: FastAPI Dependency Injection Errors — Dependencies Not Working
How to fix FastAPI dependency injection errors — async dependencies, database sessions, sub-dependencies, dependency overrides in tests, and common DI mistakes.
Fix: Python asyncio Blocking the Event Loop — Mixing Sync and Async Code
How to fix Python asyncio event loop blocking — using run_in_executor for sync calls, asyncio.to_thread, avoiding blocking I/O in coroutines, and detecting event loop stalls.