Skip to content

With Pydantic 2.13.4, a model containing a reference-style nested BaseModel passes model_validate(), but model_dump() raises PydanticSerializationError: Error calling function serialize: AttributeEr

1 solution
ranked by outcome — not votes
Accepted

A wrap model validator can bypass its handler and return a value that violates the declared type. Validation then succeeds, but serialization still uses the schema for the declared nested model. In Pydantic 2.13.4, returning None for a nonnullable model field makes the nested model_serializer run with None as its self argument.

If None is a legitimate state, declare the parent field as Reference | None. The nullable serialization branch then emits JSON null without invoking the reference serializer. Do not patch the serializer to tolerate an impossible self value. If the field really is required, pass None to the wrap validator's handler so normal validation rejects it instead of returning it unchanged.

Runnable reproduction and fix (verified on Pydantic 2.13.4):

from pydantic import BaseModel, model_serializer, model_validator

class Reference(BaseModel):
    target: str

    @model_validator(mode='wrap')
    @classmethod
    def resolve(cls, value, handler):
        return None if value is None else handler(value)

    @model_serializer
    def serialize(self):
        return {'target': self.target}

class RequiredReference(BaseModel):
    reference: Reference

required = RequiredReference.model_validate({'reference': None})
assert required.reference is None  # validation accepted the bypass
try:
    required.model_dump()
except Exception as error:
    print(type(error).__name__, error)
    # PydanticSerializationError: ... NoneType ... target

class OptionalReference(BaseModel):
    reference: Reference | None

optional = OptionalReference.model_validate({'reference': None})
assert optional.model_dump() == {'reference': None}

Audit the parent field annotation and wrap-validator return path when a nested serializer unexpectedly receives None. Successful model validation alone does not establish that a custom wrap validator returned an instance of its annotated model.