pytest-dev/pytest · error · TypeError

{} expected string as 'msg' parameter, got '{}' instead. Per

Error message

{} expected string as 'msg' parameter, got '{}' instead.
Perhaps you meant to use a mark?

What it means

OutcomeException.__init__ (base of Skipped/Failed/XFailed) requires msg to be a str or None. Passing any other type raises TypeError with a hint 'Perhaps you meant to use a mark?' because the common cause is calling pytest.skip/xfail/fail with a mark object or non-string reason.

Source

Thrown at src/_pytest/outcomes.py:23

import importlib
import sys
from typing import Any
from typing import ClassVar
from typing import NoReturn


class OutcomeException(BaseException):
    """OutcomeException and its subclass instances indicate and contain info
    about test and collection outcomes."""

    def __init__(self, msg: str | None = None, pytrace: bool = True) -> None:
        if msg is not None and not isinstance(msg, str):
            error_msg = (  # type: ignore[unreachable]
                "{} expected string as 'msg' parameter, got '{}' instead.\n"
                "Perhaps you meant to use a mark?"
            )
            raise TypeError(error_msg.format(type(self).__name__, type(msg).__name__))
        super().__init__(msg)
        self.msg = msg
        self.pytrace = pytrace

    def __repr__(self) -> str:
        if self.msg is not None:
            return self.msg
        return f"<{self.__class__.__name__} instance>"

    __str__ = __repr__


TEST_OUTCOME = (OutcomeException, Exception)


class Skipped(OutcomeException):
    # XXX hackish: on 3k we fake to live in the builtins
    # in order to have Skipped exception printing shorter/nicer

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Pass a string reason: pytest.skip('reason text')
  2. If you meant a marker, use the decorator: @pytest.mark.skip(reason='...')
  3. Convert the object to a string first: pytest.skip(str(obj))

Example fix

// before
pytest.skip(some_exception)
// after
pytest.skip(str(some_exception))
Defensive patterns

Strategy: type-guard

Validate before calling

def safe_skip(reason):
    if reason is not None and not isinstance(reason, str):
        reason = str(reason)
    import pytest; pytest.skip(reason)

Type guard

def is_str_or_none(msg) -> bool:
    return msg is None or isinstance(msg, str)

Prevention

When it happens

Trigger: pytest.skip(some_object), pytest.fail(exception_instance), or pytest.xfail(reason) where reason is not a string. Also triggered by raising Skipped/Failed directly with a non-str arg.

Common situations: Calling pytest.skip(SomeException) intending to re-raise; passing a marker decorator where a reason string is expected; mixing up pytest.mark.skip with pytest.skip.

Related errors


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