python/cpython · error · TypeError
Expected an object of type str for 'message', not {type(mess
Error message
Expected an object of type str for 'message', not {type(message).__name__!r} What it means
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__.
Source
Thrown at Lib/_py_warnings.py:791
The deprecation message passed to the decorator is saved in the
``__deprecated__`` attribute on the decorated object.
If applied to an overload, the decorator
must be after the ``@overload`` decorator for the attribute to
exist on the overload as returned by ``get_overloads()``.
See PEP 702 for details.
"""
def __init__(
self,
message: str,
/,
*,
category: type[Warning] | None = DeprecationWarning,
stacklevel: int = 1,
) -> None:
if not isinstance(message, str):
raise TypeError(
f"Expected an object of type str for 'message', not {type(message).__name__!r}"
)
self.message = message
self.category = category
self.stacklevel = stacklevel
def __call__(self, arg, /):
# Make sure the inner functions created below don't
# retain a reference to self.
msg = self.message
category = self.category
stacklevel = self.stacklevel
if category is None:
arg.__deprecated__ = msg
return arg
elif isinstance(arg, type):
import functools
from types import MethodTypeView on GitHub (pinned to bc6749cc3b)
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
Example fix
// before
@warnings.deprecated(b'use new_api')
def old_api(): ...
# after
@warnings.deprecated('old_api is deprecated; use new_api')
def old_api(): ... Defensive patterns
Strategy: type-guard
Validate before calling
import warnings msg = 'foo() is deprecated; use bar()' assert isinstance(msg, str), 'deprecated() message must be str'
Type guard
def is_deprecation_message(msg: object) -> bool:
return isinstance(msg, str) and bool(msg) Prevention
- 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
When it happens
Trigger: @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.
Common situations: 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.
Related errors
- @deprecated decorator with non-None category must be applied
- {cls.__name__}() takes no arguments
- {name!r} was slated for removal after Python {remove_formatt
- Cannot exit %r without entering first
- 'import warnings' failed; traceback:\n
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/bfec65720fe22348.
Report an issue: GitHub.