pytest-dev/pytest · error · SyntaxError
expected ; got
Error message
expected {}; got {} What it means
A SyntaxError raised by Scanner.reject when the parser expected one token type (or a set of alternatives) but the current token did not match. The message lists the expected token names joined by ' OR ' and the actually-seen token. It is the grammar-level parse error complementing the lexer-level errors 121-123.
Solutions
- Read the 'expected X OR Y; got Z' message to see which token is missing.
- Complete the expression: add the missing operand, close parenthesis, or remove the trailing operator.
- For complex expressions, build them incrementally and re-run to get the next parse hint.
Example fix
# before pytest -m 'not' # after pytest -m 'not slow'
Defensive patterns
Strategy: validation
Validate before calling
from _pytest.mark.expression import Expression
def parse_or_report(expr: str):
try:
Expression.compile(expr)
except SyntaxError as e:
return {'ok': False, 'msg': e.msg, 'offset': e.offset}
return {'ok': True} Try / catch
try:
deselect_by_mark(items, config)
except SyntaxError as e:
# 'expected X OR Y; got Z' -> tell user what token is missing
... Prevention
- Build boolean expressions from a template, not free text.
- Balance parentheses explicitly when constructing expressions.
- Test marker expressions in isolation before running the suite.
When it happens
Trigger: Malformed grammar such as `-m 'not'` (NOT followed by EOF, expected IDENT/LPAREN), `-m 'a and'` (AND with no right operand), `-m '(a'` (LPAREN never closed, expected RPAREN), or `-m 'foo('` (LPAREN not followed by IDENT for kwargs).
Common situations: Incomplete boolean expressions, dangling operators, unbalanced parentheses, or starting a kwarg list but omitting the identifier.
Related errors
- closing quote " " is missing
- escaping with "\" not supported in marker expression
- : : at column
- not a valid python identifier
- unexpected character
AI-assisted analysis of pytest-dev/pytest@0d6fbdeffa (2026-08-11).
Data as JSON: /api/errors/e8c7d925122c29b0.
Report an issue: GitHub.
Appendix: source
Thrown at src/_pytest/mark/expression.py:151
def accept(self, type: TokenType, *, reject: Literal[True]) -> Token: ...
@overload
def accept(
self, type: TokenType, *, reject: Literal[False] = False
) -> Token | None: ...
def accept(self, type: TokenType, *, reject: bool = False) -> Token | None:
if self.current.type is type:
token = self.current
if token.type is not TokenType.EOF:
self.current = next(self.tokens)
return token
if reject:
self.reject((type,))
return None
def reject(self, expected: Sequence[TokenType]) -> NoReturn:
raise SyntaxError(
"expected {}; got {}".format(
" OR ".join(type.value for type in expected),
self.current.type.value,
),
(FILE_NAME, 1, self.current.pos + 1, self.input),
)
# True, False and None are legal match expression identifiers,
# but illegal as Python identifiers. To fix this, this prefix
# is added to identifiers in the conversion to Python AST.
IDENT_PREFIX = "$"
def expression(s: Scanner) -> ast.Expression:
if s.accept(TokenType.EOF):
ret: ast.expr = ast.Constant(False)
else:View on GitHub (pinned to 0d6fbdeffa)