python/cpython · error · TypeError

message must be a string

Error message

message must be a string

What it means

Raised by warnings.filterwarnings when its message argument is not a str. The message is a regex pattern that the library compiles itself (re.compile(message, re.I)), so passing an already-compiled pattern, bytes, or None is rejected up front with TypeError('message must be a string').

Source

Thrown at Lib/_py_warnings.py:269

    return _wm._formatwarnmsg_impl(msg)


def filterwarnings(action, message="", category=Warning, module="", lineno=0,
                   append=False):
    """Insert an entry into the list of warnings filters (at the front).

    'action' -- one of "error", "ignore", "always", "all", "default", "module",
                or "once"
    'message' -- a regex that the warning message must match
    'category' -- a class that the warning must be a subclass of
    'module' -- a regex that the module name must match
    'lineno' -- an integer line number, 0 matches all warnings
    'append' -- if true, append to the list of filters
    """
    if action not in {"error", "ignore", "always", "all", "default", "module", "once"}:
        raise ValueError(f"invalid action: {action!r}")
    if not isinstance(message, str):
        raise TypeError("message must be a string")
    if not isinstance(category, type) or not issubclass(category, Warning):
        raise TypeError("category must be a Warning subclass")
    if not isinstance(module, str):
        raise TypeError("module must be a string")
    if not isinstance(lineno, int):
        raise TypeError("lineno must be an int")
    if lineno < 0:
        raise ValueError("lineno must be an int >= 0")

    if message or module:
        import re

    if message:
        message = re.compile(message, re.I)
    else:
        message = None
    if module:
        module = re.compile(module)

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass the raw pattern string: message=r'deprecated api'
  2. Unwrap compiled patterns before the call: getattr(message, 'pattern', message)
  3. If you meant 'match everything', pass the empty string '' (default) rather than None

Example fix

# before
import re
warnings.filterwarnings('ignore', message=re.compile('deprecated'))  # TypeError

# after
warnings.filterwarnings('ignore', message=r'deprecated')
Defensive patterns

Strategy: validation

Validate before calling

def to_pattern_str(msg):
    """Accept a raw string or a compiled regex, return the pattern string."""
    return getattr(msg, 'pattern', msg)

import warnings
warnings.filterwarnings('ignore', message=to_pattern_str(user_msg))

Type guard

def is_pattern_str(msg) -> bool:
    return isinstance(msg, str)

Prevention

When it happens

Trigger: warnings.filterwarnings('ignore', message=re.compile('deprecated')) — passing a compiled regex; message=b'...'; message=None explicitly (the default is '' so None passed positionally also trips it).

Common situations: Developers 'pre-compiling' the regex for performance; mixing up argument order so category/message land in the wrong slots; building filter tuples programmatically where a compiled pattern or None leaks into the message field.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/75c94cf3300b4e63. Report an issue: GitHub.