Skip to content

no-invalid-pydantic-validator-return

A terminal field validator must not return a literal contradicting its stored field type.

Why

Pydantic trusts the result of after and plain field validators without validating it against the field again. A correctly annotated validator can therefore store an integer in a string field while the model still promises a string to callers.

Fix

Return a value satisfying every targeted field's stored contract. Split validators for different field domains, or perform input conversion in a before validator so the normal field validation follows it. Preserve intentional validation chains and wire serializers.

Examples

Before — flagged A plain validator also bypasses subsequent field validation
app/models.py
from pydantic import BaseModel, field_validator
class Payload(BaseModel):
text: str
@field_validator("text", mode="plain")
@classmethod
def replace(cls, value: str) -> int:
return 3
After — preferred Input conversion is followed by ordinary field validation
app/models.py
from pydantic import BaseModel, field_validator
class Payload(BaseModel):
text: int
@field_validator("text", mode="before")
@classmethod
def replace(cls, value: str) -> str:
return "3"
Before — flagged An after validator stores an incompatible primitive unchanged
app/models.py
from pydantic import BaseModel, field_validator
class Payload(BaseModel):
text: str
@field_validator("text", mode="after")
@classmethod
def replace(cls, value: str) -> int:
return 3
After — preferred The validator output satisfies the stored field contract
app/models.py
from pydantic import BaseModel, field_validator
class Payload(BaseModel):
text: str
@field_validator("text", mode="after")
@classmethod
def replace(cls, value: str) -> str:
return "ready"