Jump to solution
Verify

The Fix

pip install pydantic==1.10.16

Based on closed pydantic/pydantic issue #9607 · PR/commit linked

Production note: This usually shows up under retries/timeouts. Treat it as a side-effect risk until you can verify behavior with a canary + real traffic.

Jump to Verify Open PR/Commit
@@ -63,7 +63,9 @@ def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any: # Even though it is the right signature for python 3.9, mypy complains with # `error: Too many arguments for "_evaluate" of "ForwardRef"` hence the cast... - return cast(Any, type_)._evaluate(globalns, localns, set()) + # Python 3.13/3.12.4+ made `recursive_guard` a kwarg, so name it explicitly to avoid: + # TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard'
repro.py
Python 3.12.4 (main, Jun 7 2024, 19:37:37) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >> import pydantic >> class Foo(pydantic.BaseModel): ... foo: "Nope" ... Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.12/site-packages/pydantic/main.py", line 286, in __new__ cls.__try_update_forward_refs__() File "/usr/local/lib/python3.12/site-packages/pydantic/main.py", line 807, in __try_update_forward_refs__ update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns, (NameError,)) File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 554, in update_model_forward_refs update_field_forward_refs(f, globalns=globalns, localns=localns) File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 520, in update_field_forward_refs field.type_ = evaluate_forwardref(field.type_, globalns, localns or None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 66, in evaluate_forwardref return cast(Any, type_)._evaluate(globalns, localns, set()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard' >>
verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.
fix.md
Option A — Upgrade to fixed release\npip install pydantic==1.10.16\nWhen NOT to use: Do not use if it changes public behavior or if the failure cannot be reproduced.\n\n

Why This Fix Works in Production

  • Trigger: TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard'
  • Mechanism: Specifies 'recursive_guard' as a keyword argument in the ForwardRef._evaluate function to address compatibility issues with Python 3.12.4.
  • Why the fix works: Specifies 'recursive_guard' as a keyword argument in the ForwardRef._evaluate function to address compatibility issues with Python 3.12.4. (first fixed release: 1.10.16).
Production impact:
  • If left unfixed, the same config can fail only in production (env differences), causing startup failures or partial feature outages.

Why This Breaks in Prod

  • Shows up under Python 3.12.4 in real deployments (not just unit tests).
  • Surfaces as: TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard'

Proof / Evidence

Discussion

High-signal excerpts from the issue thread (symptoms, repros, edge-cases).

“My solution was to downgrade python to version 3.12.3 and it worked properly”
@ellyx13 · 2024-06-08 · source
“Note: I don't reproduce with Pydantic 2. Too bad it happens right when I was working on the migration of my project :sweat_smile: .”
@k4nar · 2024-06-07 · source
“Seeing the same issue. In our case we are using pydantic 2.7.1, but are importing mergepythonclient which relies on V1.”
@andersskog · 2024-06-07 · source
“Seconded, this is an issue that specifically cropped up moving from python 3.12.3 to python 3.12.4. It also affects pydantic.v1 within pydantic==2.7.3”
@znruss · 2024-06-08 · source

Failure Signature (Search String)

  • TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard'

Error Message

Stack trace
error.txt
Error Message ------------- TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard'
Stack trace
error.txt
Error Message ------------- Python 3.12.4 (main, Jun 7 2024, 19:37:37) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >> import pydantic >> class Foo(pydantic.BaseModel): ... foo: "Nope" ... Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.12/site-packages/pydantic/main.py", line 286, in __new__ cls.__try_update_forward_refs__() File "/usr/local/lib/python3.12/site-packages/pydantic/main.py", line 807, in __try_update_forward_refs__ update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns, (NameError,)) File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 554, in update_model_forward_refs update_field_forward_refs(f, globalns=globalns, localns=localns) File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 520, in update_field_forward_refs field.type_ = evaluate_forwardref(field.type_, globalns, localns or None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 66, in evaluate_forwardref return cast(Any, type_)._evaluate(globalns, localns, set()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive ... (truncated) ...

Minimal Reproduction

repro.py
Python 3.12.4 (main, Jun 7 2024, 19:37:37) [GCC 10.2.1 20210110] on linux Type "help", "copyright", "credits" or "license" for more information. >> import pydantic >> class Foo(pydantic.BaseModel): ... foo: "Nope" ... Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.12/site-packages/pydantic/main.py", line 286, in __new__ cls.__try_update_forward_refs__() File "/usr/local/lib/python3.12/site-packages/pydantic/main.py", line 807, in __try_update_forward_refs__ update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns, (NameError,)) File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 554, in update_model_forward_refs update_field_forward_refs(f, globalns=globalns, localns=localns) File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 520, in update_field_forward_refs field.type_ = evaluate_forwardref(field.type_, globalns, localns or None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.12/site-packages/pydantic/typing.py", line 66, in evaluate_forwardref return cast(Any, type_)._evaluate(globalns, localns, set()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard' >>

Environment

  • Python: 3.12.4
  • Pydantic: 1

Fix Options (Details)

Option A — Upgrade to fixed release Safe default (recommended)

pip install pydantic==1.10.16

When NOT to use: Do not use if it changes public behavior or if the failure cannot be reproduced.

Use when you can deploy the upstream fix. It is usually lower-risk than long-lived workarounds.

Option D — Guard side-effects with OnceOnly Guardrail for side-effects

Mitigate duplicate external side-effects under retries/timeouts/agent loops by gating the operation before calling external systems.

  • Place OnceOnly between your code/agent and real side-effects (Stripe, emails, CRM, APIs).
  • Use a stable key per side-effect (e.g., customer_id + action + idempotency_key).
  • Fail-safe: configure fail-open vs fail-closed based on blast radius and spend risk.
Show example snippet (optional)
onceonly.py
from onceonly import OnceOnly import os once = OnceOnly(api_key=os.environ["ONCEONLY_API_KEY"], fail_open=True) # Stable idempotency key per real side-effect. # Use a request id / job id / webhook delivery id / Stripe event id, etc. event_id = "evt_..." # replace key = f"stripe:webhook:{event_id}" res = once.check_lock(key=key, ttl=3600) if res.duplicate: return {"status": "already_processed"} # Safe to execute the side-effect exactly once. handle_event(event_id)

See OnceOnly SDK

When NOT to use: Do not use this to hide logic bugs or data corruption. Use it to block duplicate external side-effects and enforce tool permissions/spend caps.

Fix reference: https://github.com/pydantic/pydantic/pull/9612

First fixed release: 1.10.16

Last verified: 2026-02-09. Validate in your environment.

Get updates

We publish verified fixes weekly. No spam.

Subscribe

When NOT to Use This Fix

  • Do not use if it changes public behavior or if the failure cannot be reproduced.
  • Do not use this to hide logic bugs or data corruption. Use it to block duplicate external side-effects and enforce tool permissions/spend caps.

Verify Fix

verify
Re-run the minimal reproduction on your broken version, then apply the fix and re-run.

Did This Fix Work in Your Case?

Quick signal helps us prioritize which fixes to verify and improve.

Prevention

  • Add a CI check that diffs key outputs after upgrades (OpenAPI schema snapshots, JSON payload shapes, CLI output).
  • Upgrade behind a canary and run integration tests against the canary before 100% rollout.

Version Compatibility Table

VersionStatus
1.10.16 Fixed

Related Issues

No related fixes found.

Sources

We don’t republish the full GitHub discussion text. Use the links above for context.