{"record":{"id":"88f26acd91efbb56","repo":"python/cpython","slug":"forward-reference-must-be-a-string-got-arg-r","errorCode":null,"errorMessage":"Forward reference must be a string -- got {arg!r}","messagePattern":"Forward reference must be a string -- got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/annotationlib.py","lineNumber":79,"sourceCode":"    * owner: The owning object (module, class, or function).\n    * is_argument: Does nothing, retained for compatibility.\n    * is_class: True if the forward reference was created in class scope.\n\n    \"\"\"\n\n    __slots__ = _SLOTS\n\n    def __init__(\n        self,\n        arg,\n        *,\n        module=None,\n        owner=None,\n        is_argument=True,\n        is_class=False,\n    ):\n        if not isinstance(arg, str):\n            raise TypeError(f\"Forward reference must be a string -- got {arg!r}\")\n\n        self.__arg__ = arg\n        self.__forward_is_argument__ = is_argument\n        self.__forward_is_class__ = is_class\n        self.__forward_module__ = module\n        self.__owner__ = owner\n        # These are always set to None here but may be non-None if a ForwardRef\n        # is created through __class__ assignment on a _Stringifier object.\n        self.__globals__ = None\n        # This may be either a cell object (for a ForwardRef referring to a single name)\n        # or a dict mapping cell names to cell objects (for a ForwardRef containing references\n        # to multiple names).\n        self.__cell__ = None\n        self.__extra_names__ = None\n        # These are initially None but serve as a cache and may be set to a non-None\n        # value later.\n        self.__code__ = None\n        self.__ast_node__ = None","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/annotationlib.py#L61-L97","documentation":"annotationlib.ForwardRef (the modern typing.ForwardRef) requires its positional argument to be a string containing the forward reference expression. __init__ raises TypeError immediately for any non-str argument (int, type object, None, ...).","triggerScenarios":"ForwardRef(int) or ForwardRef(None) instead of ForwardRef('int'); passing an already-evaluated type where a string is expected, e.g. building annotations programmatically: {'x': ForwardRef(list[int])}.","commonSituations":"Metaprogramming that constructs annotation objects; migrating from typing.ForwardRef misuse; wrapping values obtained from get_type_hints (already objects) back into ForwardRef.","solutions":["Pass the expression as a string: ForwardRef('int') or ForwardRef('list[int]')","If you already have the object, use it directly — no ForwardRef needed","To stringify an object, use annotationlib.get_annotations(..., format=Format.FORWARDREF) rather than hand-building"],"exampleFix":"# before\n>>> from annotationlib import ForwardRef\n>>> ForwardRef(list[int])\nTypeError: Forward reference must be a string -- got list[int]\n\n# after\n>>> ForwardRef('list[int]')\nForwardRef('list[int]')","handlingStrategy":"type-guard","validationCode":"from annotationlib import ForwardRef\n\ndef make_forwardref(arg):\n    return ForwardRef(arg if isinstance(arg, str) else str(arg))","typeGuard":"def is_forwardref_arg(arg) -> bool:\n    return isinstance(arg, str)","tryCatchPattern":"try:\n    fr = ForwardRef(value)\nexcept TypeError:\n    fr = ForwardRef(str(value))  # or use value directly if already a type","preventionTips":["ForwardRef arguments are always strings — quote the expression","Use Format.FORWARDREF via get_annotations instead of hand-constructing ForwardRef","Annotate helper APIs as (ref: str) and enforce with mypy"],"tags":["annotations","typing","forwardref","typeerror","cpython"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}