Defaults and Error Customization
This guide demonstrates how to manage optionality, default values, and error reporting using pydantic-core schemas. These tools allow you to define how validation should behave when data is missing, null, or invalid.
Allowing Null Values
To allow a field to accept None (or null in JSON), wrap the inner schema with nullable_schema.
from pydantic_core import SchemaValidator, core_schema
# Allows an integer or None
schema = core_schema.nullable_schema(schema=core_schema.int_schema())
v = SchemaValidator(schema)
assert v.validate_python(None) is None
assert v.validate_python(123) == 123
assert v.validate_python("456") == 456
Defining Default Values
Use with_default_schema to provide a default value when an input is missing. This is commonly used within typed_dict_field.
Static Defaults
Provide a fixed value using the default parameter.
from pydantic_core import SchemaValidator, core_schema
schema = core_schema.typed_dict_schema(
fields={
'x': core_schema.typed_dict_field(
schema=core_schema.with_default_schema(
schema=core_schema.str_schema(),
default='[default]'
)
),
}
)
v = SchemaValidator(schema)
assert v.validate_python({}) == {'x': '[default]'}
Dynamic Default Factories
Use default_factory for values that should be generated at runtime (e.g., timestamps or new list instances).
from pydantic_core import SchemaValidator, core_schema
schema = core_schema.with_default_schema(
schema=core_schema.int_schema(),
default_factory=lambda: 42
)
v = SchemaValidator(schema)
# When input is missing (e.g. in a TypedDict or Arguments)
# the factory is called to produce the value
Handling Validation Errors with Defaults
You can control what happens when validation fails by setting the on_error parameter in with_default_schema.
Falling Back to Default on Error
Set on_error='default' to use the provided default value if the input fails validation.
from pydantic_core import SchemaValidator, core_schema
schema = core_schema.with_default_schema(
schema=core_schema.int_schema(),
default=0,
on_error='default'
)
v = SchemaValidator(schema)
# 'abc' is not an int, so it falls back to the default
assert v.validate_python('abc') == 0
Omitting Invalid Items
Set on_error='omit' to exclude the value entirely if validation fails. This is particularly useful for filtering collections.
from pydantic_core import SchemaValidator, core_schema
# A list of integers that skips any non-integer elements
schema = core_schema.list_schema(
items_schema=core_schema.with_default_schema(
schema=core_schema.int_schema(),
on_error='omit'
)
)
v = SchemaValidator(schema)
assert v.validate_python([1, 'wrong', 3]) == [1, 3]
Customizing Error Messages
Use custom_error_schema to override the default error type and message when validation fails.
from pydantic_core import SchemaValidator, core_schema, ValidationError
import pytest
schema = core_schema.custom_error_schema(
core_schema.int_schema(),
custom_error_type='my_custom_error',
custom_error_message='This must be a number'
)
v = SchemaValidator(schema)
try:
v.validate_python('not an int')
except ValidationError as exc:
assert exc.errors(include_url=False) == [
{
'type': 'my_custom_error',
'loc': (),
'msg': 'This must be a number',
'input': 'not an int'
}
]
Distinguishing Missing from None
In advanced scenarios where None is a valid value but you need to detect when a field is truly absent, use missing_sentinel_schema within a union.
from pydantic_core import SchemaValidator, core_schema
# Allows an int, None, or the MISSING sentinel
schema = core_schema.union_schema(
choices=[
core_schema.int_schema(),
core_schema.nullable_schema(core_schema.any_schema()),
core_schema.missing_sentinel_schema(),
]
)
v = SchemaValidator(schema)
Troubleshooting and Gotchas
- Validation of Defaults: By default, the
defaultvalue is not validated against the inner schema. To ensure the default itself is valid, setvalidate_default=True.# This will raise a ValidationError during validation if the default is used
schema = core_schema.with_default_schema(
core_schema.int_schema(),
default='not_an_int',
validate_default=True
) - Conflicting Parameters: You cannot provide both
defaultanddefault_factoryin the sameWithDefaultSchema. - Required Defaults: If you set
on_error='default', you must provide either adefaultordefault_factory, otherwise aSchemaErrorwill be raised when creating the validator. - Custom Error Restrictions: If
custom_error_typematches a built-in Pydantic error type, you should not provide acustom_error_messageas it will conflict with the built-in message logic and cause aSchemaError. - Omit Limitations:
on_error='omit'cannot be used withinarguments_schema. Attempting to do so will result in aSchemaError.