Skip to main content

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 a ValidationError.
  • 'default': Use the provided default or default_factory value 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_type matches a known Pydantic error type (e.g., recursion_loop), you cannot provide a custom_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 both default and default_factory.
  • Omit Restrictions: The on_error='omit' setting cannot be used within arguments_schema parameters.
  • Context Types: custom_error_context only supports str, int, and float values.
  • Serialization: Fields set to the MISSING sentinel are automatically excluded from serialization output when using standard Pydantic serialization methods.