pytest-dev/pytest · error · TypeError

Warning must be str or Warning, got {msg!r} (type {type(msg)

Error message

Warning must be str or Warning, got {msg!r} (type {type(msg).__name__})

What it means

When a recorded warning is a UserWarning whose first argument is not a str, pytest raises TypeError. This guards against calls like warnings.warn(123) that produce warnings CPython cannot filter correctly (see cpython#103577). Warnings must carry a str message or be a proper Warning instance.

Source

Thrown at src/_pytest/recwarn.py:376

            # While this can be considered a bug in CPython, we put guards in
            # pytest as the error message produced without this check in place
            # is confusing (#10865).
            for w in self:
                if type(w.message) is not UserWarning:
                    # If the warning was of an incorrect type then `warnings.warn()`
                    # creates a UserWarning. Any other warning must have been specified
                    # explicitly.
                    continue
                if not w.message.args:
                    # UserWarning() without arguments must have been specified explicitly.
                    continue
                msg = w.message.args[0]
                if isinstance(msg, str):
                    continue
                # It's possible that UserWarning was explicitly specified, and
                # its first argument was not a string. But that case can't be
                # distinguished from an invalid type.
                raise TypeError(
                    f"Warning must be str or Warning, got {msg!r} (type {type(msg).__name__})"
                )

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Ensure warnings.warn is called with a str message: warnings.warn('text', Category).
  2. Wrap non-str values in str() at the call site: warnings.warn(str(obj)).
  3. If the bad warning comes from a dependency you cannot change, filter or patch it upstream of the pytest.warns block.
  4. Construct a proper Warning instance with a str arg instead of passing a bare object.

Example fix

// before
import warnings
warnings.warn(123)
// after
import warnings
warnings.warn('123')
Defensive patterns

Strategy: validation

Validate before calling

# before emitting, coerce non-str messages to str
msg = 123
warnings.warn(str(msg), UserWarning)

Type guard

def is_valid_warning_message(msg) -> TypeGuard[str]:
    return isinstance(msg, str)

Prevention

When it happens

Trigger: Code under test (or a dependency) calls warnings.warn(123), warnings.warn(some_object), or warnings.warn() with a non-str, non-Warning first arg, while inside a pytest.warns/recwarn context. Hits recwarn.py:376 during __exit__ re-emission.

Common situations: Third-party library emits an invalid warning; test helper passes a non-str message; a refactor changed a warning message variable to a non-str type.

Related errors


AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04). Data as JSON: /data/errors/bb147564fbedcd9f.json. Report an issue: GitHub.