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

  1. Import TypedDict from typing_extensions (from typing_extensions import TypedDict) and keep using Required/NotRequired from typing_extensions.
  2. Upgrade the runtime to Python >= 3.11 where typing.TypedDict reflects the special forms.
  3. Drop Required/NotRequired from the TypedDict and encode optionality via total=False / total=True or separate TypedDicts.
  4. 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 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


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 fields

View on GitHub (pinned to cc13d1b8c9)