pydantic/pydantic · error · PydanticSerializationUnexpectedValue

Expected ` ` but got ` ` with value `' '` - serialized…

Error message

Expected `{tp}` but got `{type(path)}` with value `'{path}'` - serialized value may not be as expected.

What it means

PydanticSerializationUnexpectedValue raised in ser_path, the serializer pydantic attaches to path types. At serialization time it checks isinstance(path, (tp, str)); if the value held by the field is neither the declared path type (tp) nor a str, it raises this. Like error 42 for IPs, this indicates the field was populated with a value of the wrong type (e.g. via model_construct() or direct __dict__ mutation), so JSON serialization would emit an unexpected representation.

Solutions

  1. Construct the model via Model.model_validate(data) instead of model_construct() so the path field is coerced to a real path object.
  2. If model_construct() is required, pre-coerce: pathlib.Path(value) before assignment.
  3. Make the field type a Union (pathlib.Path | None) and ensure None is handled in serialization.
  4. Add a model_validator(mode='before') that normalizes raw values to paths.

Example fix

// before
m = Cfg.model_construct(p='/tmp/x')  # stored as str, not Path
m.model_dump_json()  # may raise if tp is PurePath and value is str-only edge

// after
m = Cfg.model_validate({'p': '/tmp/x'})  # validated to PurePath
m.model_dump_json()
Defensive patterns

Strategy: validation

Validate before calling

import pathlib

def coerce_path(value):
    if isinstance(value, (pathlib.PurePath, str)):
        return value
    if value is None:
        return value
    return pathlib.Path(value)

# data['p'] = coerce_path(data['p']); then use model_validate, not model_construct

Type guard

import pathlib

def is_path_or_str(value, tp=pathlib.PurePath) -> bool:
    return isinstance(value, (tp, str))

Try / catch

from pydantic_core import PydanticSerializationUnexpectedValue

try:
    obj.model_dump_json()
except PydanticSerializationUnexpectedValue:
    obj = M.model_validate(obj.model_dump())  # re-coerce
    obj.model_dump_json()

Prevention

When it happens

Trigger: model_construct(p=42) on a pathlib.Path field then model_dump_json(); assigning an int or dict to a path field after validation; a custom validator returning a non-path, non-str object; ORM integration that returns a raw column value into a path field without coercion.

Common situations: Using model_construct() for speed and feeding DB rows whose path column is None or an int; mutating the field post-validation; deserializing from msgpack/protobuf where the path is a bytes blob that was not decoded.

Related errors


AI-assisted analysis of pydantic/pydantic@cc13d1b8c9 (2026-08-11). Data as JSON: /api/errors/848b963a6af22ce5. Report an issue: GitHub.

Appendix: source

Thrown at pydantic/_internal/_generate_schema.py:619

            try:
                if path_type is bytes:
                    if isinstance(input_value, bytes):
                        try:
                            input_value = input_value.decode()
                        except UnicodeDecodeError as e:
                            raise PydanticCustomError('bytes_type', 'Input must be valid bytes') from e
                    else:
                        raise PydanticCustomError('bytes_type', 'Input must be bytes')
                elif not isinstance(input_value, str):
                    raise PydanticCustomError('path_type', 'Input is not a valid path')

                return path_constructor(input_value)  # type: ignore
            except TypeError as e:
                raise PydanticCustomError('path_type', 'Input is not a valid path') from e

        def ser_path(path: Any, info: core_schema.SerializationInfo) -> str | os.PathLike[Any]:
            if not isinstance(path, (tp, str)):
                raise PydanticSerializationUnexpectedValue(
                    f"Expected `{tp}` but got `{type(path)}` with value `'{path}'` - serialized value may not be as expected."
                )
            if info.mode == 'python':
                return path
            return str(path)

        instance_schema = core_schema.json_or_python_schema(
            json_schema=core_schema.no_info_after_validator_function(path_validator, lax_inner_schema),
            python_schema=core_schema.is_instance_schema(tp),
        )

        schema = core_schema.lax_or_strict_schema(
            lax_schema=core_schema.union_schema(
                [
                    instance_schema,
                    core_schema.no_info_after_validator_function(path_validator, strict_inner_schema),
                ],
                custom_error_type='path_type',

View on GitHub (pinned to cc13d1b8c9)