Default Values and Error Customization
To configure fallback logic for missing data and override standard validation error messages, this project provides specialized core schemas: WithDefaultSchema, CustomErrorSchema, and MissingSentinelSchema. These allow you to define how validators should behave when input is absent or invalid.
Providing Default Values
Use with_default_schema to provide a fallback value when a field is missing from the input or when validation fails (depending on the on_error configuration).
from pydantic_core import SchemaValidator, core_schema
# Basic default value for a TypedDict field
v = SchemaValidator(
core_schema.typed_dict_schema(
fields={
'x': core_schema.typed_dict_field(schema=core_schema.str_schema()),
'y': core_schema.typed_dict_field(
schema=core_schema.with_default_schema(
schema=core_schema.str_schema(),
default='[default]'
)
),
}
)
)
assert v.validate_python({'x': 'hello'}) == {'x': 'hello', 'y': '[default]'}
Dynamic Defaults with Factories
If the default value needs to be generated at runtime (e.g., a timestamp or a new list), use default_factory. You can also configure the factory to receive the validated data from other fields by setting default_factory_takes_data=True.
# Default factory that uses data from another field
v = SchemaValidator(
core_schema.typed_dict_schema(
fields={
'a': core_schema.typed_dict_field(schema=core_schema.int_schema()),
'b': core_schema.typed_dict_field(
schema=core_schema.with_default_schema(
schema=core_schema.int_schema(),
default_factory=lambda data: data['a'] * 2,
default_factory_takes_data=True
)
),
}
)
)
assert v.validate_python({'a': 10}) == {'a': 10, 'b': 20}
Handling Errors with Fallbacks
The on_error parameter determines the behavior when the underlying schema fails validation:
'raise'(default): Raise aValidationError.'default': Use the provideddefaultordefault_factoryvalue instead of raising an error.'omit': Silently drop the item. This is particularly useful in collections like lists or tuples.
# Using on_error='omit' to filter out invalid items in a list
v = SchemaValidator(
core_schema.list_schema(
items_schema=core_schema.with_default_schema(
schema=core_schema.int_schema(),
on_error='omit'
)
)
)
# 'wrong' is not an int, so it is omitted
assert v.validate_python([1, 'wrong', 3]) == [1, 3]
Customizing Validation Errors
Use custom_error_schema to override the standard error message and type raised by a schema. This is useful for providing domain-specific error messages or integrating with external error reporting systems.
from pydantic_core import SchemaValidator, ValidationError, core_schema
v = SchemaValidator(
core_schema.custom_error_schema(
schema=core_schema.int_schema(),
custom_error_type='invalid_age',
custom_error_message='The provided age must be a valid integer',
custom_error_context={'min_age': 18}
)
)
try:
v.validate_python('not-an-int')
except ValidationError as exc:
assert exc.errors(include_url=False) == [
{
'type': 'invalid_age',
'loc': (),
'msg': 'The provided age must be a valid integer',
'input': 'not-an-int',
'ctx': {'min_age': 18}
}
]
[!IMPORTANT] If
custom_error_typematches a known Pydantic error type (e.g.,recursion_loop), you cannot provide acustom_error_message. The validator will automatically use the standard message associated with that type.
Distinguishing Unset from Null with MISSING
In complex scenarios where you need to distinguish between a field being explicitly set to None versus being absent, use the MISSING sentinel and MissingSentinelSchema.
from pydantic_core import MISSING, SchemaValidator, core_schema
# A schema that accepts either an integer or the MISSING sentinel
v = SchemaValidator(
core_schema.union_schema([
core_schema.int_schema(),
core_schema.missing_sentinel_schema()
])
)
# Explicitly passing MISSING is allowed
assert v.validate_python(MISSING) is MISSING
assert v.validate_python(123) == 123
This pattern is frequently used in pydantic.BaseModel to allow fields to survive validation as "not provided" without defaulting to None.
Troubleshooting
- Mutually Exclusive Fields: In
WithDefaultSchema, you cannot provide bothdefaultanddefault_factory. - Omit Restrictions: The
on_error='omit'setting cannot be used withinarguments_schemaparameters. - Context Types:
custom_error_contextonly supportsstr,int, andfloatvalues. - Serialization: Fields set to the
MISSINGsentinel are automatically excluded from serialization output when using standard Pydantic serialization methods.