python/cpython · error · NameError

name ' ' is not defined

Error message

name '{name:.200}' is not defined

What it means

When ForwardRef.evaluate() resolves a simple identifier, it looks in locals, then globals, then builtins; if the name is found nowhere and the target format is not a forward-ref-preserving format, it raises NameError with the standard message and a `name=` keyword carrying the unresolved identifier. This mirrors normal Python name resolution for annotation strings.

Solutions

  1. Import or define the referenced name so it is resolvable at evaluation time
  2. Pass the defining module's namespace: ForwardRef('X').evaluate(globals=vars(module))
  3. Guard imports used only for annotations under TYPE_CHECKING and evaluate lazily only when needed
  4. Catch NameError and fall back to Format.FORWARDREF/STRING format to defer resolution

Example fix

# before
from __future__ import annotations

def parse() -> Result: ...   # NameError: name 'Result' is not defined (on get_type_hints)

# after
from __future__ import annotations
from mymod import Result

def parse() -> Result: ...
Defensive patterns

Strategy: try-catch

Validate before calling

def evaluate_ref(fr, module_globals):
    try:
        return fr.evaluate(globals=module_globals)
    except NameError:
        return None  # defer: unresolved symbol

Type guard

def name_resolvable(name: str, globals_ns: dict, locals_ns: dict = None) -> bool:
    import builtins
    scopes = [locals_ns or {}, globals_ns, vars(builtins)]
    return any(name in s for s in scopes)

Try / catch

from annotationlib import ForwardRef, Format

try:
    obj = fr.evaluate(globals=ns)
except NameError as e:
    if getattr(e, 'name', None) == target_name:
        obj = fr.evaluate(globals=ns, format=Format.FORWARDREF)  # keep as ForwardRef
    else:
        raise

Prevention

When it happens

Trigger: ForwardRef('Missing').evaluate(); typing.get_type_hymes on annotations referencing names deleted/renamed/moved; class-level annotations like 'def f(self) -> Result:' where Result was never imported or was defined after use under `from __future__ import annotations`.

Common situations: Refactors that move classes without updating string annotations; TYPE_CHECKING-only imports used at runtime; deserialized/pickled annotation strings evaluated in a fresh namespace lacking the original module globals.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/bdc5112cecdaad65. Report an issue: GitHub.

Appendix: source

Thrown at Lib/annotationlib.py:203

                    pass
                else:
                    locals.setdefault(cell_name, cell_value)

        if self.__extra_names__:
            locals.update(self.__extra_names__)

        arg = self.__forward_arg__
        if arg.isidentifier() and not keyword.iskeyword(arg):
            if arg in locals:
                return locals[arg]
            elif arg in globals:
                return globals[arg]
            elif hasattr(builtins, arg):
                return getattr(builtins, arg)
            elif is_forwardref_format:
                return self
            else:
                raise NameError(_NAME_ERROR_MSG.format(name=arg), name=arg)
        else:
            code = self.__forward_code__
            try:
                return eval(code, globals=globals, locals=locals)
            except Exception:
                if not is_forwardref_format:
                    raise

            # All variables, in scoping order, should be checked before
            # triggering __missing__ to create a _Stringifier.
            new_locals = _StringifierDict(
                {**builtins.__dict__, **globals, **locals},
                globals=globals,
                owner=owner,
                is_class=self.__forward_is_class__,
                format=format,
            )
            try:

View on GitHub (pinned to bc6749cc3b)