Handling Defaults and Custom Errors
Handling default values and customizing error responses are core tasks when defining schemas. This guide demonstrates how to use WithDefaultSchema to manage missing or invalid data and CustomErrorSchema to provide domain-specific error feedback.
Providing Default Values
Use WithDefaultSchema to specify a value to use when a field is missing or when validation fails.
Static Default Values
The most common use case is providing a static value for a missing field.
from pydantic_core import SchemaValidator, core_schema
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]'
)
),
}
)
)
# 'y' is missing, so the default is used
assert v.validate_python({'x': 'x'}) == {'x': 'x', 'y': '[default]'}
Dynamic Defaults with Factories
For values that should be generated at runtime (like timestamps or new list instances), use default_factory.
v = SchemaValidator(
core_schema.with_default_schema(
schema=core_schema.int_schema(),
default_factory=lambda: 17
)
)
assert v.validate_python(None) == 17
If your factory needs access to the rest of the data being validated, set default_factory_takes_data=True.
Validating Default Values
By default, the default or the result of default_factory is not validated. Set validate_default=True to ensure the default value also conforms to the inner schema.
from pydantic_core import ValidationError
import pytest
v = SchemaValidator(
core_schema.with_default_schema(
core_schema.int_schema(),
default='not-an-int',
validate_default=True
)
)
# Validation fails because the default value 'not-an-int' is not a valid integer
with pytest.raises(ValidationError):
v.validate_python(None)
Handling Validation Errors
The on_error parameter in WithDefaultSchema determines how the validator behaves when the input data is present but invalid.
Using Defaults on Failure
Set on_error='default' to gracefully fall back to the default value if validation fails.
v = SchemaValidator(
core_schema.with_default_schema(
schema=core_schema.int_schema(),
default=2,
on_error='default'
)
)
# 'wrong' is not an int, so it returns the default value 2
assert v.validate_python('wrong') == 2
Omitting Invalid Items
Set on_error='omit' to remove the item entirely if it fails validation. This is particularly useful for filtering collections.
v = SchemaValidator(
core_schema.list_schema(
items_schema=core_schema.with_default_schema(
schema=core_schema.int_schema(),
on_error='omit'
)
)
)
# 'wrong' is omitted from the resulting list
assert v.validate_python([1, 'wrong', 3]) == [1, 3]
Customizing Error Messages
Use CustomErrorSchema to override the standard error type and message generated by an inner schema.
v = SchemaValidator(
core_schema.custom_error_schema(
core_schema.int_schema(),
custom_error_type='my_custom_error',
custom_error_message='Please provide a valid number'
)
)
with pytest.raises(ValidationError) as exc_info:
v.validate_python('not-a-number')
assert exc_info.value.errors(include_url=False) == [
{
'type': 'my_custom_error',
'loc': (),
'msg': 'Please provide a valid number',
'input': 'not-a-number'
}
]
Using Known Error Types
If you use a custom_error_type that matches one of Pydantic's built-in error types, you should not provide a custom_error_message. The validator will automatically use the standard message for that type.
# This uses the standard 'recursion_loop' message
v = SchemaValidator(
core_schema.custom_error_schema(
core_schema.int_schema(),
'recursion_loop'
)
)
Troubleshooting
Mutable Defaults
Pydantic-core automatically deep-copies mutable default values (like lists or dictionaries) to prevent shared state between different validation results.
stored_empty_list = []
v = SchemaValidator(
core_schema.with_default_schema(
schema=core_schema.list_schema(items_schema=core_schema.int_schema()),
default=stored_empty_list,
)
)
m1 = v.validate_python(None)
assert m1 is not stored_empty_list # The default was copied
InvalidSchema Errors
The InvalidSchema class is a placeholder used for internal signaling. If you attempt to build a SchemaValidator using core_schema.invalid_schema(), it will raise a SchemaError.
from pydantic_core import SchemaError
# This will raise: SchemaError: Cannot construct schema with `InvalidSchema` member.
with pytest.raises(SchemaError):
SchemaValidator(core_schema.invalid_schema())
Configuration Conflicts
- You cannot provide both
defaultanddefault_factoryin the sameWithDefaultSchema. on_error='default'requires either adefaultordefault_factoryto be present.on_error='omit'cannot be used within anarguments_schema.