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

  1. Read the column number in the message; it points at the exact offending character in your `-m`/`-k` value.
  2. Quote the whole expression on the shell so spaces/operators are preserved, e.g. `pytest -m 'not slow'`.
  3. Refer to the marker-expression grammar (and/orand/not/ident/kwargs) and simplify the expression until it parses.
  4. 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

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


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