{"record":{"id":"bfec65720fe22348","repo":"python/cpython","slug":"expected-an-object-of-type-str-for-message-not","errorCode":null,"errorMessage":"Expected an object of type str for 'message', not {type(message).__name__!r}","messagePattern":"Expected an object of type str for 'message', not (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/_py_warnings.py","lineNumber":791,"sourceCode":"    The deprecation message passed to the decorator is saved in the\n    ``__deprecated__`` attribute on the decorated object.\n    If applied to an overload, the decorator\n    must be after the ``@overload`` decorator for the attribute to\n    exist on the overload as returned by ``get_overloads()``.\n\n    See PEP 702 for details.\n\n    \"\"\"\n    def __init__(\n        self,\n        message: str,\n        /,\n        *,\n        category: type[Warning] | None = DeprecationWarning,\n        stacklevel: int = 1,\n    ) -> None:\n        if not isinstance(message, str):\n            raise TypeError(\n                f\"Expected an object of type str for 'message', not {type(message).__name__!r}\"\n            )\n        self.message = message\n        self.category = category\n        self.stacklevel = stacklevel\n\n    def __call__(self, arg, /):\n        # Make sure the inner functions created below don't\n        # retain a reference to self.\n        msg = self.message\n        category = self.category\n        stacklevel = self.stacklevel\n        if category is None:\n            arg.__deprecated__ = msg\n            return arg\n        elif isinstance(arg, type):\n            import functools\n            from types import MethodType","sourceCodeStart":773,"sourceCodeEnd":809,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/_py_warnings.py#L773-L809","documentation":"The warnings.deprecated decorator (PEP 702) requires its message argument to be a plain str. Because the message is stored on the wrapped object as __deprecated__ and consumed by static analyzers and IDEs, non-string values (bytes, exceptions, f-string-like objects, None) are rejected immediately with a TypeError in __init__.","triggerScenarios":"@warnings.deprecated(b'use new_api instead'); @warnings.deprecated(None); @warnings.deprecated(some_exception_instance); passing a lazy formatting callable or a translated message object instead of a formatted str.","commonSituations":"Porting old code that used functools.wraps-style decorators accepting arbitrary objects; messages built from bytes or config blobs; backporting code to runtimes where warnings.deprecated does not exist and substituting a look-alike shim; accidentally passing the docstring keyword instead of message.","solutions":["Pass an f-string or literal str: @warnings.deprecated('foo() is deprecated; use bar()')","If the message is dynamic, format it to str before decorating: @warnings.deprecated(f'use {replacement}')","If targeting Python < 3.13, use typing_extensions.deprecated, which enforces the same str rule"],"exampleFix":"// before\n@warnings.deprecated(b'use new_api')\ndef old_api(): ...\n\n# after\n@warnings.deprecated('old_api is deprecated; use new_api')\ndef old_api(): ...","handlingStrategy":"type-guard","validationCode":"import warnings\nmsg = 'foo() is deprecated; use bar()'\nassert isinstance(msg, str), 'deprecated() message must be str'","typeGuard":"def is_deprecation_message(msg: object) -> bool:\n    return isinstance(msg, str) and bool(msg)","tryCatchPattern":null,"preventionTips":["Format messages fully before decorating; deprecated() takes no lazy callables","On older Pythons use typing_extensions.deprecated with the same str rule","Add a lint/test that imports the module, so bad decorators fail at import time"],"tags":["warnings","deprecated","typeerror","decorator","stdlib"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}