pydantic/pydantic · error · TypeError
You should use `typing_extensions.TypedDict` instead of…
Error message
You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.11. Without it, there is no way to reflect Required/NotRequired keys.
What it means
TypeError raised by pydantic.v1.create_model_from_typeddict when the TypedDict is a 'legacy' (PEP 589) TypedDict AND at least one annotation uses the special forms Required / NotRequired / ReadOnly. Those special forms are only reflectable on typing.TypedDict from Python 3.11 onward; on older runtimes pydantic v1 cannot tell which keys are required, so it asks the user to switch to typing_extensions.TypedDict. The check is is_legacy_typeddict(typeddict_cls) combined with any(is_typeddict_special(t) for t in __annotations__.values()).
Solutions
- Import TypedDict from typing_extensions (from typing_extensions import TypedDict) and keep using Required/NotRequired from typing_extensions.
- Upgrade the runtime to Python >= 3.11 where typing.TypedDict reflects the special forms.
- Drop Required/NotRequired from the TypedDict and encode optionality via total=False / total=True or separate TypedDicts.
- Avoid create_model_from_typeddict for that class; build the model with create_model and explicit field definitions.
Example fix
// before
from typing import TypedDict
from typing_extensions import Required, NotRequired
from pydantic.v1 import create_model_from_typeddict
class User(TypedDict):
id: Required[int]
nickname: NotRequired[str]
M = create_model_from_typeddict(User) # TypeError on py<3.11
// after
from typing_extensions import TypedDict, Required, NotRequired
from pydantic.v1 import create_model_from_typeddict
class User(TypedDict):
id: Required[int]
nickname: NotRequired[str]
M = create_model_from_typeddict(User) Defensive patterns
Strategy: type-guard
Validate before calling
null
Type guard
import typing, typing_extensions
def typeddict_supports_special_forms(typeddict_cls: type) -> bool:
# Safe when TypedDict comes from typing_extensions, or runtime is >= 3.11
is_ext = typeddict_cls is getattr(typing_extensions, 'TypedDict', object) or issubclass(typeddict_cls, getattr(typing_extensions, 'TypedDict', object))
return is_ext or sys.version_info >= (3, 11) Try / catch
null
Prevention
- When using Required/NotRequired/ReadOnly, import both TypedDict and those special forms from typing_extensions.
- Keep the supported-Python floor at 3.11+ if you want to use typing.TypedDict with these special forms.
- In shared libraries, gate Required/NotRequired usage behind a typing_extensions import unconditionally.
- Lint against 'from typing import TypedDict' when targeting Python < 3.11.
When it happens
Trigger: On Python < 3.11, calling create_model_from_typeddict on a typing.TypedDict subclass that uses Required[...] or NotRequired[...] (or ReadOnly[...]) as an annotation. The legacy TypedDict does not store the required/optional semantics for those special forms, so the guard raises TypeError.
Common situations: Migrating a TypedDict to use Required/NotRequired while the project still supports Python 3.10; CI matrix covering 3.10/3.9 while the dev worked on 3.12; mixing typing.TypedDict with typing_extensions.Required without also importing TypedDict from typing_extensions; dependency on a library that hands you a legacy TypedDict.
Related errors
- You should use `typing_extensions.TypedDict` instead of…
- typed-dict-version
- Cannot specify both `config` and keyword arguments
- duplicate validator function
- Inconsistent hierarchy, no C3 MRO is possible
AI-assisted analysis of pydantic/pydantic@cc13d1b8c9 (2026-08-11).
Data as JSON: /api/errors/b0afade11a9d59c2.
Report an issue: GitHub.
Appendix: source
Thrown at pydantic/v1/annotated_types.py:44
) -> Type['BaseModel']:
"""
Create a `BaseModel` based on the fields of a `TypedDict`.
Since `typing.TypedDict` in Python 3.8 does not store runtime information about optional keys,
we raise an error if this happens (see https://bugs.python.org/issue38834).
"""
field_definitions: Dict[str, Any]
# Best case scenario: with python 3.9+ or when `TypedDict` is imported from `typing_extensions`
if not hasattr(typeddict_cls, '__required_keys__'):
raise TypeError(
'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.9.2. '
'Without it, there is no way to differentiate required and optional fields when subclassed.'
)
if is_legacy_typeddict(typeddict_cls) and any(
is_typeddict_special(t) for t in typeddict_cls.__annotations__.values()
):
raise TypeError(
'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.11. '
'Without it, there is no way to reflect Required/NotRequired keys.'
)
required_keys: FrozenSet[str] = typeddict_cls.__required_keys__ # type: ignore[attr-defined]
field_definitions = {
field_name: (field_type, Required if field_name in required_keys else None)
for field_name, field_type in typeddict_cls.__annotations__.items()
}
return create_model(typeddict_cls.__name__, **kwargs, **field_definitions)
def create_model_from_namedtuple(namedtuple_cls: Type['NamedTuple'], **kwargs: Any) -> Type['BaseModel']:
"""
Create a `BaseModel` based on the fields of a named tuple.
A named tuple can be created with `typing.NamedTuple` and declared annotations
but also with `collections.namedtuple`, in this case we consider all fieldsView on GitHub (pinned to cc13d1b8c9)