pydantic/pydantic · error · RecursionError
{e.args[0]} If you made use of an implicit recursive type al
Error message
{e.args[0]}
If you made use of an implicit recursive type alias (e.g. `MyType = list['MyType']), consider using PEP 695 type aliases instead. For more details, refer to the documentation: https://pydantic.dev/docs/validation/{version_short()}/concepts/types/#named-recursive-types What it means
A `RecursionError` (with a note pointing to PEP 695) re-raised from `eval_type` when forward-reference evaluation blows the recursion limit. The classic cause is an implicit recursive type alias such as `MyType = list['MyType']`, where resolving the alias references itself indefinitely under the legacy alias semantics.
Source
Thrown at pydantic/_internal/_typing_extra.py:392
message = f'Unable to evaluate type annotation {value.__forward_arg__!r}.'
else:
message = f'Unable to evaluate type annotation {value!r}.'
if sys.version_info >= (3, 11):
e.add_note(message)
raise
else:
raise TypeError(message) from e
except RecursionError as e:
message = (
"If you made use of an implicit recursive type alias (e.g. `MyType = list['MyType']), "
'consider using PEP 695 type aliases instead. For more details, refer to the documentation: '
f'https://pydantic.dev/docs/validation/{version_short()}/concepts/types/#named-recursive-types'
)
if sys.version_info >= (3, 11):
e.add_note(message)
raise
else:
raise RecursionError(f'{e.args[0]}\n{message}')
@deprecated(
'`eval_type_lenient()` is deprecated, use `try_eval_type()` instead.',
category=None,
)
def eval_type_lenient(
value: Any,
globalns: GlobalsNamespace | None = None,
localns: MappingNamespace | None = None,
) -> Any: # pragma: no cover
ev, _ = try_eval_type(value, globalns, localns)
return ev
def _eval_type(
value: Any,
globalns: GlobalsNamespace | None = None,View on GitHub (pinned to 2e5f0e2b42)
Solutions
- Switch to a PEP 695 type alias: `type MyType = list['MyType']` (lazy, recursion-safe).
- Alternatively model the recursion with a self-referencing BaseModel: `class Node(BaseModel): children: list['Node']`.
- If you must use the legacy form, make the alias refer to a forward-ref class name rather than to itself.
Example fix
# before
MyType = list['MyType'] # implicit recursive alias
class M(BaseModel):
x: MyType # RecursionError
# after (PEP 695, Python 3.12+)
type MyType = list['MyType']
class M(BaseModel):
x: MyType Defensive patterns
Strategy: validation
Type guard
import sys
def safe_alias_eval(alias_str, ns):
sys.setrecursionlimit(200)
try:
eval(alias_str, ns)
return True
except RecursionError:
return False Prevention
- Prefer PEP 695 `type` aliases for recursive types.
- Model graph/tree structures with self-referencing BaseModels.
- Test recursive type definitions in isolation before wiring them in.
When it happens
Trigger: Defining `MyType = list['MyType']` (pre-PEP-695 style) and using it on a Pydantic field. Each evaluation of the alias re-substitutes itself, recursing until the interpreter's limit is hit.
Common situations: Building tree/graph/JSON-like recursive structures; porting recursive type aliases between Python versions; using older tutorials that predate PEP 695.
Related errors
- Too few arguments for {cls}; actual {len(args)}, expected at
- Unable to evaluate type annotation {value!r}.
- field "{self.name}" not yet prepared so type is still a Forw
- Cannot parameterize a concrete instantiation of a generic mo
- Type parameters should be placed on typing.Generic, not Gene
AI-assisted analysis of pydantic/pydantic@2e5f0e2b42 (2026-08-04).
Data as JSON: /data/errors/2923991ea67cf2b4.json.
Report an issue: GitHub.