Skip to main content

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 default value is not validated against the inner schema. To ensure the default itself is valid, set validate_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 default and default_factory in the same WithDefaultSchema.
  • Required Defaults: If you set on_error='default', you must provide either a default or default_factory, otherwise a SchemaError will be raised when creating the validator.
  • Custom Error Restrictions: If custom_error_type matches a built-in Pydantic error type, you should not provide a custom_error_message as it will conflict with the built-in message logic and cause a SchemaError.
  • Omit Limitations: on_error='omit' cannot be used within arguments_schema. Attempting to do so will result in a SchemaError.