pytest-dev/pytest · error · TypeError
{e} - maybe you meant pytest.mark.skipif?
Error message
{e} - maybe you meant pytest.mark.skipif? What it means
pytest.mark.skip accepts only reason= and allow_module_level=; it does not take a condition. evaluate_skip_marks() constructs Skip(*mark.args, **mark.kwargs), so passing positional condition arguments (the skipif signature) makes Skip()'s dataclass constructor raise TypeError. pytest catches that TypeError and re-raises it with a hint pointing to skipif, since the most common cause is confusing the two decorators.
Source
Thrown at src/_pytest/skipping.py:192
else:
conditions = (mark.kwargs["condition"],)
# Unconditional.
if not conditions:
reason = mark.kwargs.get("reason", "")
return Skip(reason)
# If any of the conditions are true.
for condition in conditions:
result, reason = evaluate_condition(item, mark, condition)
if result:
return Skip(reason)
for mark in item.iter_markers(name="skip"):
try:
return Skip(*mark.args, **mark.kwargs)
except TypeError as e:
raise TypeError(str(e) + " - maybe you meant pytest.mark.skipif?") from None
return None
@dataclasses.dataclass(frozen=True)
class Xfail:
"""The result of evaluate_xfail_marks()."""
__slots__ = ("raises", "reason", "run", "strict")
reason: str
run: bool
strict: bool
raises: (
type[BaseException]
| tuple[type[BaseException], ...]
| AbstractRaises[BaseException]
| NoneView on GitHub (pinned to 0d6fbdeffa)
Solutions
- Switch the decorator to pytest.mark.skipif for conditional skipping: @pytest.mark.skipif(sys.platform == 'win32', reason='...').
- If you want unconditional skip, drop the condition arg: @pytest.mark.skip(reason='...').
- Pass reason only as a keyword: @pytest.mark.skip(reason='why') — never positional condition args.
Example fix
# before @pytest.mark.skip(sys.platform == "win32", reason="windows-only") def test_x(): ... # after @pytest.mark.skipif(sys.platform == "win32", reason="windows-only") def test_x(): ...
Defensive patterns
Strategy: validation
Validate before calling
# In conftest.py — collect-time check that surfaces the misuse early.
import pytest
def pytest_collectstart(collector):
# Inspect skip marks for positional args (the skipif signature).
for mark in getattr(collector, "iter_markers", lambda **k: [])() or []:
if getattr(mark, "name", None) == "skip" and mark.args:
raise pytest.UsageError(
"pytest.mark.skip got positional args; did you mean skipif?"
) Type guard
# Detect the misuse statically in a lint/check script:
import ast
def skip_has_positional_args(decorator: ast.AST) -> bool:
return (
isinstance(decorator, ast.Call)
and isinstance(decorator.func, ast.Attribute)
and decorator.func.attr == "skip"
and len(decorator.args) >= 1
) Try / catch
# Not recommended to catch — fix the decorator. If migrating many tests:
import re, pathlib
for p in pathlib.Path("tests").rglob("*.py"):
t = p.read_text()
t2 = re.sub(r"@pytest\.mark\.skip\(([^)]+),\s*reason=", r"@pytest.mark.skipif(\1, reason=", t)
p.write_text(t2) Prevention
- Use skipif for conditional skipping; reserve skip for unconditional skips with only reason=.
- Always pass reason as a keyword argument to skip.
- Search the codebase for `mark.skip(` with a comma to find existing misuses.
When it happens
Trigger: Writing @pytest.mark.skip(sys.platform == 'win32', reason='windows-only') or @pytest.mark.skip('some condition') — i.e. giving skip the positional args that belong to skipif. Also triggered by passing an unexpected kwarg to skip.
Common situations: Copy-pasting a skipif example and changing the decorator name to skip; reading docs for skipif and applying them to skip; migrating a conditional skip written as `if cond: pytest.skip()` into a decorator form and getting the marker wrong.
Related errors
- pytest_markeval_namespace() needs to return a dict, got {dic
- is not a string or pytest.mark.* Marker
- module {modname!r} has __version__ {verattr!r}, required is:
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/0386ce194ddf069c.
Report an issue: GitHub.