Controlling Error Flow with Defaults and Omissions
To handle validation failures gracefully in pydantic-core, you can use WithDefaultSchema to define fallback behaviors. This allows you to either return a default value or omit the invalid field/item entirely instead of raising a validation error.
Omitting Invalid Items from Collections
You can use on_error='omit' within a WithDefaultSchema to skip items in a list or fields in a TypedDict that fail validation.
from pydantic_core import SchemaValidator, core_schema
# Define a list where invalid integers are skipped instead of raising an error
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 result
assert v.validate_python([1, 'wrong', 3]) == [1, 3]
# Define a TypedDict where an invalid field is omitted
v_dict = 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(),
on_error='omit'
),
required=False,
),
}
)
)
# 'y' is omitted because 42 is not a string
assert v_dict.validate_python({'x': 'hello', 'y': 42}) == {'x': 'hello'}
Falling Back to a Default Value on Error
Setting on_error='default' ensures that if the primary schema validation fails, the validator returns the provided default or the result of a default_factory.
from pydantic_core import SchemaValidator, core_schema
v = SchemaValidator(
core_schema.with_default_schema(
schema=core_schema.int_schema(),
default=0,
on_error='default'
)
)
# Returns the default value 0 because 'not-an-int' fails int_schema validation
assert v.validate_python('not-an-int') == 0
Triggering Defaults Programmatically in Validators
You can use the PydanticUseDefault exception inside a validator function to explicitly signal that Pydantic should fall back to the field's default value. This is useful for custom logic where certain inputs (like empty strings) should be treated as "missing" and replaced by a default.
from typing import Any
from pydantic_core import SchemaValidator, core_schema, PydanticUseDefault
def val_func(v: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> Any:
# If the input is an empty string, trigger the default value
if isinstance(v, str) and v == '':
raise PydanticUseDefault
return handler(v)
validator = SchemaValidator(
core_schema.with_default_schema(
schema=core_schema.no_info_wrap_validator_function(
val_func,
core_schema.int_schema()
),
default=10
)
)
assert validator.validate_python('1') == 1
assert validator.validate_python('') == 10
Skipping Items Programmatically with PydanticOmit
The PydanticOmit exception allows you to skip an item from a collection or a field from a model based on custom logic within a validator.
from pydantic_core import SchemaValidator, core_schema, PydanticOmit
def omit_specific_value(v, info):
if v == 'skip-me':
raise PydanticOmit
return v
v = SchemaValidator(
core_schema.list_schema(
items_schema=core_schema.with_info_plain_validator_function(
omit_specific_value
)
)
)
# 'skip-me' is removed from the resulting list
assert v.validate_python(['a', 'skip-me', 'b']) == ['a', 'b']
Troubleshooting and Constraints
Uncaught Control Flow Exceptions
PydanticUseDefault and PydanticOmit must be caught by a parent schema that supports them. If they are raised in a context where they cannot be handled (e.g., a top-level validator without a default), a SchemaError is raised.
# This will raise a SchemaError because there is no WithDefaultSchema to catch it
v = SchemaValidator(core_schema.with_info_plain_validator_function(
lambda v, info: exec("raise PydanticOmit")
))
# Raises: SchemaError: Uncaught Omit error, please check your usage of `default` validators.
Default Value Requirements
When using on_error='default', you must provide either default or default_factory. Providing both in the same WithDefaultSchema is not permitted.
Default Factories with Data
If your default depends on other data in the object, set default_factory_takes_data=True.
def factory_with_data(data: dict[str, Any]) -> Any:
return data.get('other_field')
schema = core_schema.with_default_schema(
schema=core_schema.int_schema(),
default_factory=factory_with_data,
default_factory_takes_data=True
)