Core functionality for Pydantic validation and serialization
—
Comprehensive error handling system with detailed error information, location tracking, and support for custom error messages and error type definitions. Pydantic Core provides rich error reporting that helps developers understand validation failures.
Core exception classes for different types of validation and serialization errors.
class ValidationError(ValueError):
"""
Main validation error with detailed error information.
Raised when data validation fails, containing detailed information
about what went wrong, where it went wrong, and the context.
"""
def errors(self) -> list[ErrorDetails]:
"""
Get detailed information about all validation errors.
Returns:
List of error details with location, message, and context
"""
def error_count(self) -> int:
"""
Get the total number of validation errors.
Returns:
Number of errors
"""
def __str__(self) -> str:
"""
Get a human-readable error message.
Returns:
Formatted error message
"""
class PydanticCustomError(ValueError):
"""
Custom validation error with user-defined messages.
Allows developers to create validation errors with custom
error types and message templates.
"""
def __init__(self, error_type: str, message_template: str, context: dict = None):
"""
Create a custom validation error.
Args:
error_type: String identifier for the error type
message_template: Template string for the error message
context: Context values for message template formatting
"""
class PydanticKnownError(ValueError):
"""
Pre-defined validation error for known error types.
Used for standard validation errors with pre-defined
error types and message templates.
"""
def __init__(self, error_type: ErrorType, context: dict = None):
"""
Create a known validation error.
Args:
error_type: Pre-defined error type
context: Context values for message formatting
"""
class SchemaError(Exception):
"""
Schema definition and building errors.
Raised when there are problems with schema definitions,
such as invalid schema structures or conflicting constraints.
"""
class PydanticSerializationError(ValueError):
"""
Serialization process errors.
Raised when data cannot be serialized due to unsupported
types or serialization constraints.
"""
class PydanticSerializationUnexpectedValue(PydanticSerializationError):
"""
Unexpected value during serialization.
Raised when the serializer encounters a value that doesn't
match the expected schema during serialization.
"""TypedDict structures that provide detailed error information.
ErrorDetails = TypedDict('ErrorDetails', {
'type': str, # Error type identifier
'loc': tuple[int | str, ...], # Location tuple (field path)
'msg': str, # Human-readable error message
'input': Any, # The input value that caused the error
'ctx': NotRequired[dict[str, Any]], # Additional error context
'url': NotRequired[str] # Documentation URL for the error
})
InitErrorDetails = TypedDict('InitErrorDetails', {
'type': Union[str, PydanticCustomError], # Error type or custom error
'loc': NotRequired[tuple[int | str, ...]], # Location tuple
'input': Any, # The input value that caused the error
'ctx': NotRequired[dict[str, Any]] # Additional error context
})
ErrorTypeInfo = TypedDict('ErrorTypeInfo', {
'type': ErrorType, # The error type
'message_template_python': str, # Message template for Python input
'example_message_python': str, # Example message for Python input
'message_template_json': NotRequired[str], # Message template for JSON input
'example_message_json': NotRequired[str], # Example message for JSON input
'example_context': dict[str, Any] | None # Example context values
})Pre-defined error types for common validation failures.
ErrorType = Literal[
# General validation errors
'missing', 'extra_forbidden', 'value_error', 'assertion_error', 'type_error',
# Boolean validation errors
'bool_parsing', 'bool_type',
# Integer validation errors
'int_parsing', 'int_type', 'int_parsing_size', 'int_from_float',
'int_greater_than', 'int_greater_than_equal', 'int_less_than',
'int_less_than_equal', 'int_multiple_of',
# Float validation errors
'float_parsing', 'float_type', 'float_finite_number',
'float_greater_than', 'float_greater_than_equal', 'float_less_than',
'float_less_than_equal', 'float_multiple_of',
# String validation errors
'string_type', 'string_too_short', 'string_too_long', 'string_pattern_mismatch',
'string_unicode', 'string_sub_type',
# Bytes validation errors
'bytes_type', 'bytes_too_short', 'bytes_too_long',
# Date and time validation errors
'date_parsing', 'date_type', 'date_future', 'date_past',
'time_parsing', 'time_type',
'datetime_parsing', 'datetime_type', 'datetime_future', 'datetime_past',
'datetime_aware', 'datetime_naive',
'timedelta_parsing', 'timedelta_type',
'timezone_naive', 'timezone_aware',
# Collection validation errors
'list_type', 'tuple_type', 'set_type', 'frozenset_type', 'dict_type',
'too_short', 'too_long',
# Advanced type validation errors
'is_instance_of', 'is_subclass_of', 'callable_type',
'literal_error', 'enum_parsing',
'union_tag_invalid', 'union_tag_not_found',
# URL validation errors
'url_parsing', 'url_syntax_violation', 'url_too_long', 'url_scheme',
# UUID validation errors
'uuid_parsing', 'uuid_version',
# Decimal validation errors
'decimal_parsing', 'decimal_type', 'decimal_max_digits',
'decimal_max_places', 'decimal_whole_digits',
# JSON validation errors
'json_invalid', 'json_type',
# Model validation errors
'model_type', 'model_attributes_type', 'model_class',
'dataclass_type', 'dataclass_exact_type',
# Custom and function validation errors
'custom_error', 'validator_error', 'function_no_validator',
'function_after_field_validator', 'function_before_field_validator',
'function_wrap_validator', 'function_plain_validator',
# And many more...
]Functions for working with error information.
def list_all_errors() -> list[ErrorTypeInfo]:
"""
Get information about all possible validation error types.
Returns:
List of error type information including message templates and examples
"""from pydantic_core import SchemaValidator, ValidationError
from pydantic_core.core_schema import dict_schema, str_schema, int_schema
# Create a schema
schema = dict_schema({
'name': str_schema(min_length=2),
'age': int_schema(ge=0, le=120),
'email': str_schema()
})
validator = SchemaValidator(schema)
# Try to validate invalid data
try:
invalid_data = {
'name': 'A', # Too short
'age': 150, # Too high
'email': '' # Empty string
}
validator.validate_python(invalid_data)
except ValidationError as e:
print(f"Validation failed with {e.error_count()} errors:")
for error in e.errors():
location = ' -> '.join(map(str, error['loc']))
print(f" {location}: {error['msg']}")
print(f" Input: {error['input']}")
print(f" Type: {error['type']}")
if 'ctx' in error:
print(f" Context: {error['ctx']}")
print()
# Output:
# Validation failed with 3 errors:
# name: String should have at least 2 characters
# Input: A
# Type: string_too_short
# Context: {'min_length': 2}
#
# age: Input should be less than or equal to 120
# Input: 150
# Type: int_less_than_equal
# Context: {'le': 120}
#
# email: String should have at least 1 character
# Input:
# Type: string_too_short
# Context: {'min_length': 1}from pydantic_core import SchemaValidator, ValidationError
from pydantic_core.core_schema import dict_schema, list_schema, str_schema, int_schema
# Schema for nested data
schema = dict_schema({
'users': list_schema(
dict_schema({
'name': str_schema(min_length=1),
'age': int_schema(ge=0)
})
)
})
validator = SchemaValidator(schema)
try:
data = {
'users': [
{'name': 'Alice', 'age': 25}, # Valid
{'name': '', 'age': -5}, # Invalid name and age
{'name': 'Charlie', 'age': 30} # Valid
]
}
validator.validate_python(data)
except ValidationError as e:
for error in e.errors():
# Location shows exact path to error
location_path = ' -> '.join(map(str, error['loc']))
print(f"{location_path}: {error['msg']}")
# Output:
# users -> 1 -> name: String should have at least 1 character
# users -> 1 -> age: Input should be greater than or equal to 0from pydantic_core import PydanticCustomError, SchemaValidator, ValidationError
from pydantic_core.core_schema import with_info_plain_validator_function
def validate_username(value):
"""Custom validator with custom error messages."""
if not isinstance(value, str):
raise PydanticCustomError(
'username_type',
'Username must be a string, got {input_type}',
{'input_type': type(value).__name__}
)
if len(value) < 3:
raise PydanticCustomError(
'username_too_short',
'Username must be at least 3 characters long, got {input_length}',
{'input_length': len(value)}
)
if not value.isalnum():
raise PydanticCustomError(
'username_invalid_chars',
'Username must contain only letters and numbers'
)
return value
# Create schema with custom validator
schema = with_info_plain_validator_function(validate_username)
validator = SchemaValidator(schema)
try:
validator.validate_python('ab!') # Invalid characters
except ValidationError as e:
for error in e.errors():
print(f"Error: {error['msg']}")
print(f"Type: {error['type']}")
if 'ctx' in error:
print(f"Context: {error['ctx']}")
# Output:
# Error: Username must contain only letters and numbers
# Type: username_invalid_charsfrom pydantic_core import PydanticKnownError, SchemaValidator, ValidationError
from pydantic_core.core_schema import with_info_plain_validator_function
def validate_even_number(value):
"""Validator using known error types."""
if not isinstance(value, int):
raise PydanticKnownError('int_type')
if value % 2 != 0:
raise PydanticKnownError(
'int_multiple_of',
{'multiple_of': 2}
)
return value
schema = with_info_plain_validator_function(validate_even_number)
validator = SchemaValidator(schema)
try:
validator.validate_python(7) # Odd number
except ValidationError as e:
for error in e.errors():
print(f"Error: {error['msg']}")
print(f"Type: {error['type']}")
print(f"Context: {error.get('ctx', {})}")
# Output will use the standard int_multiple_of error messagefrom pydantic_core import (
SchemaSerializer,
PydanticSerializationError,
PydanticSerializationUnexpectedValue
)
from pydantic_core.core_schema import dict_schema, str_schema
# Create serializer
schema = dict_schema({'name': str_schema()})
serializer = SchemaSerializer(schema)
try:
# Try to serialize invalid data structure
invalid_data = {'name': object()} # object() is not serializable
serializer.to_json(invalid_data)
except PydanticSerializationError as e:
print(f"Serialization error: {e}")
# Handle with fallback
def fallback_handler(obj):
return str(obj)
try:
result = serializer.to_json(
{'name': object()},
fallback=fallback_handler
)
print(f"Serialized with fallback: {result}")
except PydanticSerializationError as e:
print(f"Still failed: {e}")from pydantic_core import list_all_errors
# Get information about all possible error types
all_errors = list_all_errors()
# Find specific error type information
string_errors = [
error for error in all_errors
if error['type'].startswith('string_')
]
for error_info in string_errors[:3]: # Show first 3 string errors
print(f"Type: {error_info['type']}")
print(f"Template: {error_info['message_template_python']}")
print(f"Example: {error_info['example_message_python']}")
if error_info['example_context']:
print(f"Context: {error_info['example_context']}")
print()
# Output shows error type information for string validation errorsfrom pydantic_core import SchemaValidator, ValidationError
from pydantic_core.core_schema import dict_schema, str_schema, int_schema, list_schema
# Complex schema for demonstration
schema = dict_schema({
'personal': dict_schema({
'name': str_schema(min_length=1),
'age': int_schema(ge=0, le=120)
}),
'contacts': list_schema(
dict_schema({
'type': str_schema(),
'value': str_schema(min_length=1)
})
)
})
validator = SchemaValidator(schema)
try:
data = {
'personal': {'name': '', 'age': -1},
'contacts': [
{'type': 'email', 'value': ''},
{'type': 'phone', 'value': 'valid-phone'}
]
}
validator.validate_python(data)
except ValidationError as e:
# Analyze errors by location
errors_by_section = {}
for error in e.errors():
section = error['loc'][0] if error['loc'] else 'root'
if section not in errors_by_section:
errors_by_section[section] = []
errors_by_section[section].append(error)
for section, errors in errors_by_section.items():
print(f"\nErrors in {section} section:")
for error in errors:
location = ' -> '.join(map(str, error['loc'][1:])) # Skip section name
print(f" {location}: {error['msg']}")
# Count errors by type
error_types = {}
for error in e.errors():
error_type = error['type']
error_types[error_type] = error_types.get(error_type, 0) + 1
print(f"\nError type distribution:")
for error_type, count in error_types.items():
print(f" {error_type}: {count}")Install with Tessl CLI
npx tessl i tessl/pypi-pydantic-core