pytest-dev/pytest · error · UsageError
{exc_message}: {e.text}: at column {e.offset}: {e.msg}
Error message
{exc_message}: {e.text}: at column {e.offset}: {e.msg} What it means
This is pytest's top-level wrapper that converts a SyntaxError raised while compiling a `-k`/`-m` match expression into a UsageError. The `exc_message` prefix (e.g. "Wrong expression passed to '-m' option") tells you which CLI flag caused it; the rest reports the offending text, 1-based column, and the underlying parser message. It fires at collection time, before any test runs.
Source
Thrown at src/_pytest/mark/__init__.py:283
expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'")
remaining: list[Item] = []
deselected: list[Item] = []
for item in items:
if expr.evaluate(MarkMatcher.from_markers(item.iter_markers())):
remaining.append(item)
else:
deselected.append(item)
if deselected:
config.hook.pytest_deselected(items=deselected)
items[:] = remaining
def _parse_expression(expr: str, exc_message: str) -> Expression:
try:
return Expression.compile(expr)
except SyntaxError as e:
raise UsageError(
f"{exc_message}: {e.text}: at column {e.offset}: {e.msg}"
) from None
def pytest_collection_modifyitems(items: list[Item], config: Config) -> None:
deselect_by_keyword(items, config)
deselect_by_mark(items, config)
def pytest_configure(config: Config) -> None:
config.stash[old_mark_config_key] = MARK_GEN._config
MARK_GEN._config = config
# Eagerly validate the value; it is only read lazily during collection.
config.getini(EMPTY_PARAMETERSET_OPTION)
def pytest_unconfigure(config: Config) -> None:View on GitHub (pinned to 98b357f69e)
Solutions
- Read the column number in the message; it points at the exact offending character in your `-m`/`-k` value.
- Quote the whole expression on the shell so spaces/operators are preserved, e.g. `pytest -m 'not slow'`.
- Refer to the marker-expression grammar (and/orand/not/ident/kwargs) and simplify the expression until it parses.
- If the expression comes from a variable, print it before invocation to verify it is what you expect.
Example fix
# before pytest -m slow and( integration or slow ) # after pytest -m 'slow and (integration or slow)'
Defensive patterns
Strategy: validation
Validate before calling
from _pytest.mark.expression import Expression
def validate_marker_expr(expr: str) -> None:
Expression.compile(expr) # raises SyntaxError before pytest runs Type guard
def is_valid_expr(expr: object) -> bool:
if not isinstance(expr, str) or not expr.strip():
return False
try:
Expression.compile(expr)
except SyntaxError:
return False
return True Try / catch
from _pytest.config import UsageError
try:
pytest.main(['-m', user_expr, *tests])
except UsageError as e:
print(f'Bad -m expression: {e}') Prevention
- Always quote `-m`/`-k` arguments on the shell.
- Validate the expression string in CI config before invoking pytest.
- Keep marker expressions simple; prefer multiple `-m` flags over complex boolean expressions.
When it happens
Trigger: Passing a syntactically invalid string to `pytest -k <expr>`, `pytest -m <expr>`, or to `deselect_by_mark`/`deselect_by_keyword` indirectly. Any SyntaxError emitted by `Expression.compile()` (scanner or parser) gets rewrapped here with the flag-specific prefix.
Common situations: Typing a marker filter on the command line with stray characters, unmatched parens, or quoting issues inside the shell; CI configs that interpolate a variable into `-m "$MARKS"` that expands to garbage; upgrading pytest and using new kwarg syntax in `-m` incorrectly.
Related errors
- -o/--override-ini expects option=value style (got: {ini_conf
- Directory '{rootdir}' not found. Check your '--rootdir' opti
- --pdbcls: could not import {value!r}: {exc}
- plugin {name} cannot be disabled
- Blocking conftest files using -p is not supported: -p no:{na
AI-assisted analysis of pytest-dev/pytest@98b357f69e (2026-08-04).
Data as JSON: /data/errors/871351ccf4f92d5d.json.
Report an issue: GitHub.