python/cpython · error · NameError
name '{name:.200}' 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.
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)
Solutions
- Import or define the referenced name so it is resolvable at evaluation time
- Pass the defining module's namespace: ForwardRef('X').evaluate(globals=vars(module))
- Guard imports used only for annotations under TYPE_CHECKING and evaluate lazily only when needed
- 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
- Evaluate annotations with the defining module's namespace: globals=vars(sys.modules[cls.__module__])
- Do not delete or rename annotated names without grepping string annotations
- Catch NameError and retry with Format.FORWARDREF to defer resolution
- Keep runtime-needed imports outside `if TYPE_CHECKING:` blocks
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
- Forward reference must be a string -- got {arg!r}
- Cannot subclass ForwardRef
- Forward reference must be an expression -- got {arg!r}
- Callable must be used as Callable[[arg, ...], result].
- Expected a list of types, an ellipsis, ParamSpec, or Concate
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/bdc5112cecdaad65.
Report an issue: GitHub.