pytest-dev/pytest · error · UsageError
: : at column
Error message
{exc_message}: {e.text}: at column {e.offset}: {e.msg} What it means
Raised as a UsageError when pytest fails to compile a marker or keyword expression supplied via -m / -k (or the API equivalent). The original SyntaxError from the expression parser is unwrapped and re-thrown with a pytest-level message, prefixing the location-specific details (offending text, column offset, message). It stops collection because pytest cannot decide which tests to select.
Solutions
- Re-run pytest and read the full message: it names the offending text, the 1-based column, and the parser's complaint.
- Balance parentheses/quotes in the -m or -k value and quote the whole expression in the shell.
- If the expression is built dynamically, validate it with `Expression.compile(expr)` in a try/except before passing it to pytest.
- Simplify the expression to a single marker name and re-add operators one at a time to isolate the syntax fault.
Example fix
# before pytest -m 'not (smoke' # after pytest -m 'not smoke'
Defensive patterns
Strategy: validation
Validate before calling
from _pytest.mark.expression import Expression
def safe_marker_expr(expr: str) -> bool:
try:
Expression.compile(expr)
return True
except SyntaxError:
return False Try / catch
from _pytest.config import UsageError
try:
deselect_by_keyword(items, config)
except UsageError as e:
# report the bad expression to the user / fall back to no filter
print(f'invalid selection expression: {e}') Prevention
- Treat -m/-k values as data, not Python; validate before passing to pytest APIs.
- Quote CLI expressions to avoid shell interference.
- Build marker expressions from a known-good vocabulary of marker names.
When it happens
Trigger: Calling pytest with `-m 'not (smoke'` (unbalanced parens), `-k 'test_ and'` (trailing operator), or programmatically invoking deselect_by_mark / deselect_by_keyword with a malformed expression string. Any string that Expression.compile cannot parse triggers it.
Common situations: Typos in CLI selection expressions, copy-pasting a partial expression, using shell quoting that strips parentheses, or upgrading pytest where the marker expression grammar changed (e.g. introducing kwargs).
Related errors
- closing quote " " is missing
- escaping with "\" not supported in marker expression
- expected ; got
- not a valid python identifier
- unexpected character
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/871351ccf4f92d5d.
Report an issue: GitHub.
Appendix: 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 0d6fbdeffa)