{"record":{"id":"0108ed6e7340aa8c","repo":"python/cpython","slug":"forward-reference-must-be-an-expression-got-ar","errorCode":null,"errorMessage":"Forward reference must be an expression -- got {arg!r}","messagePattern":"Forward reference must be an expression -- got (.+?)","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"Lib/annotationlib.py","lineNumber":266,"sourceCode":"            if names:\n                visitor = _ExtraNameFixer(names)\n                ast_expr = ast.parse(resolved_str, mode=\"eval\").body\n                node = visitor.visit(ast_expr)\n                resolved_str = ast.unparse(node)\n\n            self.__resolved_str_cache__ = resolved_str\n\n        return self.__resolved_str_cache__\n\n    @property\n    def __forward_code__(self):\n        if self.__code__ is not None:\n            return self.__code__\n        arg = self.__forward_arg__\n        try:\n            self.__code__ = compile(_rewrite_star_unpack(arg), \"<string>\", \"eval\")\n        except SyntaxError:\n            raise SyntaxError(f\"Forward reference must be an expression -- got {arg!r}\")\n        return self.__code__\n\n    def __eq__(self, other):\n        if not isinstance(other, ForwardRef):\n            return NotImplemented\n        return (\n            self.__forward_arg__ == other.__forward_arg__\n            and self.__forward_module__ == other.__forward_module__\n            # Use \"is\" here because we use id() for this in __hash__\n            # because dictionaries are not hashable.\n            and self.__globals__ is other.__globals__\n            and self.__forward_is_class__ == other.__forward_is_class__\n            # Two separate cells are always considered unequal in forward refs.\n            and (\n                {name: id(cell) for name, cell in self.__cell__.items()}\n                == {name: id(cell) for name, cell in other.__cell__.items()}\n                if isinstance(self.__cell__, dict) and isinstance(other.__cell__, dict)\n                else self.__cell__ is other.__cell__","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/annotationlib.py#L248-L284","documentation":"ForwardRef.__forward_code__ compiles the forward reference string with compile(..., 'eval') to build __code__. If the string is not a valid Python expression (e.g. it contains a statement like an assignment, or a lambda with ';'), the SyntaxError from compile is replaced by this clearer SyntaxError. It means the annotation string stored in a ForwardRef (typically from a quoted annotation or __annotations__ under PEP 563) cannot be evaluated later.","triggerScenarios":"ForwardRef('x = 1').__forward_code__; ForwardRef('import os'); ForwardRef('x; y'); a quoted annotation like def f(x: \"a = b\") evaluated via ForwardRef.evaluate() or typing.get_type_hints(); annotations produced by broken source generation or manual string building.","commonSituations":"Hand-written string annotations containing statements instead of expressions; code generators emitting malformed annotation strings; get_type_hints() on modules whose annotations were tampered with; typo in a quoted annotation such as a stray '='.","solutions":["Fix the annotation string so it is a single valid Python expression (remove assignments, imports, semicolons).","If the string comes from dynamic code, validate it first with ast.parse(s, mode='eval') and reject or repair non-expressions.","Catch SyntaxError around ForwardRef.evaluate()/get_type_hints() and report which annotation string failed (the message includes the offending repr)."],"exampleFix":"// before\ndef f(x: \"n = 10\"): ...\ntyping.get_type_hints(f)  # SyntaxError: Forward reference must be an expression -- got 'n = 10'\n\n// after\ndef f(x: \"int\"): ...\ntyping.get_type_hints(f)  # {'x': <class 'int'>}","handlingStrategy":"try-catch","validationCode":"import ast\n\ndef is_valid_forward_ref_expr(s: str) -> bool:\n    try:\n        ast.parse(s, mode='eval')\n        return True\n    except SyntaxError:\n        return False","typeGuard":"from annotationlib import ForwardRef\n\ndef is_forward_ref(x) -> bool:\n    return isinstance(x, ForwardRef)","tryCatchPattern":"from annotationlib import ForwardRef\n\ntry:\n    value = ref.evaluate()\nexcept SyntaxError as e:\n    # e.g. log the offending annotation and skip it\n    print(f'skipping bad annotation: {e}')","preventionTips":["Keep quoted annotations as single expressions; never statements","Validate generated annotation strings with ast.parse(mode='eval') before use","Run typing.get_type_hints() in tests over all annotated modules to catch bad strings early"],"tags":["python","typing","forward-reference","annotations","syntax"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}