Skip to content

no-before-validator-input-mutation

Preserve raw inputs in Pydantic before validators.

Why

Treat raw before-validator inputs as borrowed data. They can be caller-owned or reused by another validation branch; mutation can leak normalization into aliases and leave data changed after validation fails. Return replacements even when a particular caller supplies fresh data.

Fix

Return a normalized replacement mapping or collection. Copy the changed path before updating nested values; a fresh outer copy permits outer writes but retains shared descendants.

Examples

Before — flagged Removing a raw entry changes caller data
app/records.py
from pydantic import BaseModel, model_validator
class Record(BaseModel):
name: str
@model_validator(mode="before")
@classmethod
def normalize(cls, data: dict[str, object]):
data.pop("legacy", None)
return data
After — preferred A copied outer mapping can be updated locally
app/records.py
from pydantic import BaseModel, model_validator
class Record(BaseModel):
name: str
@model_validator(mode="before")
@classmethod
def normalize(cls, data: dict[str, object]):
data = dict(data)
data["name"] = "new"
return data
Before — flagged Before-validation changes the original mapping
app/records.py
from pydantic import BaseModel, model_validator
class Record(BaseModel):
name: str
@model_validator(mode="before")
@classmethod
def normalize(cls, data: dict[str, object]):
data["name"] = "new"
return data
After — preferred Return normalized data without changing the original
app/records.py
from pydantic import BaseModel, model_validator
class Record(BaseModel):
name: str
@model_validator(mode="before")
@classmethod
def normalize(cls, data: dict[str, object]):
return {**data, "name": "new"}