Skip to main content

Validating Lists and Sequences

In pydantic-core, list validation is managed through the ListSchema structure. This schema allows for defining variable-length sequences where every item must conform to a specific sub-schema. The primary way to construct these schemas is via the list_schema helper function found in pydantic_core.core_schema.

Defining List Schemas

A basic list schema requires an items_schema, which defines the validation logic for every element within the list. When a list is validated, pydantic-core iterates through the input, applies the items_schema to each element, and returns a new list containing the validated items.

from pydantic_core import SchemaValidator, core_schema

# A schema for a list of integers
schema = core_schema.list_schema(core_schema.int_schema())
v = SchemaValidator(schema)

assert v.validate_python(['1', 2, 3.0]) == [1, 2, 3]

Length Constraints

The ListSchema supports min_length and max_length constraints. These constraints are evaluated after the individual items have been validated. This is a critical distinction when using features like item omission.

from pydantic_core import SchemaValidator, core_schema

schema = core_schema.list_schema(
core_schema.int_schema(),
min_length=2,
max_length=5
)
v = SchemaValidator(schema)

# Valid: length is 3
assert v.validate_python([1, 2, 3]) == [1, 2, 3]

# Invalid: length is 1
# Raises ValidationError: List should have at least 2 items

Interaction with Item Omission

If the items_schema is configured to omit items on error (using on_error='omit' in a with_default_schema), the length constraints are checked against the final list after the invalid items have been removed.

As seen in pydantic-core/tests/validators/test_list.py:

from pydantic_core import SchemaValidator, core_schema as cs

v = SchemaValidator(
cs.list_schema(
items_schema=cs.with_default_schema(schema=cs.int_schema(), on_error='omit'),
max_length=4
)
)

# Input has 5 items, but 'x' is omitted, resulting in a list of length 4.
# This passes the max_length=4 constraint.
assert v.validate_python([1, 2, 3, 'x', 4]) == [1, 2, 3, 4]

# Input has 5 valid items, resulting in length 5.
# This fails the max_length=4 constraint.
# v.validate_python([1, 2, 3, 4, 5]) -> ValidationError

Strict vs. Lax Validation

The behavior of list validation changes significantly based on the strict setting:

  • Lax Mode (Default): pydantic-core accepts various iterable types and coerces them into a list. This includes tuple, set, frozenset, deque, generators, and dictionary keys or values.
  • Strict Mode: Only actual Python list instances are accepted.

Example of strict mode from pydantic-core/tests/validators/test_list.py:

from pydantic_core import SchemaValidator, core_schema as cs
import pytest

v = SchemaValidator(cs.list_schema(items_schema=cs.int_schema(), strict=True))

# Lists are still accepted and items are validated/coerced
assert v.validate_python([1, 2, '33']) == [1, 2, 33]

# Tuples are rejected in strict mode
with pytest.raises(ValidationError) as exc_info:
v.validate_python((1, 2, 3))

Validation Control with fail_fast

By default, pydantic-core validates every item in the list and collects all encountered errors. If you prefer to stop validation immediately upon the first error, you can set fail_fast=True.

This is particularly useful for performance when validating very large lists where a single failure renders the entire list invalid.

from pydantic_core import SchemaValidator, core_schema

# fail_fast=True stops at the first error
schema = core_schema.list_schema(core_schema.int_schema(), fail_fast=True)
v = SchemaValidator(schema)

# If [1, 'not-a-number', 'also-bad'] is provided,
# only the error for 'not-a-number' is reported.

Implementation Details

Copying Behavior

In pydantic-core, lists are always copied during validation. Even if the input is already a list and no items are changed or coerced, v.validate_python(data) will return a new list object. This ensures that the original input remains immutable relative to the validation process.

Generator Validation

When a generator is passed to a list validator in lax mode, the validator consumes the generator to create the list. If the generator raises an exception during iteration, pydantic-core wraps this in a ValidationError with a type of iteration_error.