Cerberus: Lightweight Schema Validator
Cerberus is a lightweight, extensible data validation library for Python dictionaries. It allows you to define a schema and then validate whether a given dictionary conforms to that schema, supporting various data types, constraints, and custom validation rules. The current version is 1.3.8, and it maintains a stable release cadence with incremental updates within the 1.x series.
Common errors
-
{'field_name': ['unknown field']}cause A document being validated contains a key (field) that is not defined in the validation schema, and the `allow_unknown` option is set to `False` (which is the default behavior in Cerberus).fixEither add the field to the schema definition, or explicitly set `allow_unknown=True` on the `Validator` instance or within the schema rules for that specific dictionary level. For example: `v = Validator(schema, allow_unknown=True)` or `schema = {'my_dict': {'type': 'dict', 'allow_unknown': True}}`. -
DocumentError: 'document is not a dict'
cause The Cerberus `Validator` expects the top-level document passed to its `validate()` method to be a dictionary (a mapping), but it received a list or another non-dictionary type.fixEnsure that the `document` argument passed to `validator.validate()` is always a dictionary. If you need to validate a list of dictionaries, you should typically iterate over the list and validate each dictionary individually against a schema defined for a single item, or wrap the list in a dictionary (e.g., `{'items': my_list}`) and define a schema for the 'items' key. -
cerberus.schema.SchemaError: {'field_name': ['unknown rule']}cause A rule specified within the validation schema is not recognized by Cerberus, either due to a typo in the rule name, an attempt to use an unsupported rule, or a custom validation rule that has not been properly registered with a custom `Validator` subclass. This can also occur if a schema itself is malformed, such as expecting a dictionary for a schema definition but receiving a non-dictionary value.fixReview the schema for typos in rule names. If using a custom rule, ensure it is correctly defined within a custom `Validator` class and that an instance of this custom `Validator` is used. If the `SchemaError` points to a malformed schema structure, correct the schema's type or nested structure. -
Validator.validate() returns False but no exception is raised / how to get errors
cause By design, Cerberus does not raise exceptions for most validation failures. Instead, the `validate()` method returns `False` if validation fails, and all encountered errors are collected and stored in the `validator.errors` attribute.fixAfter calling `validator.validate(document)`, always check its boolean return value. If `False`, access the `validator.errors` attribute to retrieve a dictionary containing detailed messages about all validation issues found in the document. For example: `v = Validator(schema); if not v.validate(document): print(v.errors)`.
Warnings
- breaking Starting with Cerberus 1.3.0, the `allow_unknown` validator option (which controls whether unknown fields in a document are allowed) now defaults to `False`. Previously, it implicitly allowed unknown fields.
- gotcha Since Cerberus 1.1, `type` schema rules are processed *before* `coerce` rules. This means type validation is performed on the original value, not the coerced value.
- gotcha The `required: True` and `nullable: True` schema rules are orthogonal. `required: True` means the key *must* exist in the document. `nullable: True` means that if the key exists, its value *can* be `None`.
Install
-
pip install cerberus
Imports
- Validator
from cerberus import Validator
Quickstart
from cerberus import Validator
schema = {
'name': {'type': 'string', 'minlength': 3, 'maxlength': 10, 'required': True},
'age': {'type': 'integer', 'min': 0, 'max': 99, 'nullable': True},
'email': {'type': 'string', 'regex': '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'}
}
document = {
'name': 'John Doe',
'age': 30,
'email': 'john.doe@example.com'
}
v = Validator(schema)
if v.validate(document):
print('Document is valid.')
print(f'Normalized document: {v.normalized(document)}')
else:
print('Document is invalid.')
print(f'Errors: {v.errors}')
# Example with an invalid document
invalid_document = {
'name': 'Jo',
'age': 150,
'city': 'New York' # Unknown field
}
if not v.validate(invalid_document):
print(f'Invalid document errors: {v.errors}')