pytest-dev/pytest · error · SyntaxError

expected {}; got {}

Error message

expected {}; got {}

What it means

Generic parser error from `Scanner.reject`: the parser expected one of a set of token types (e.g. identifier, parenthesis, EOF) but the current token was something else. The message lists what was expected (joined by ` OR `) and what was actually seen.

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 98b357f69e)

Solutions

  1. Match the expected vs got token types in the message to locate the missing/extra token.
  2. Balance every `(` with a `)` and ensure each `and`/`or` has operands on both sides.
  3. Simplify the expression to the smallest failing form to isolate the syntax issue.

Example fix

# before
pytest -m 'slow and'
# after
pytest -m 'slow and integration'
Defensive patterns

Strategy: validation

Validate before calling

from _pytest.mark.expression import Expression
def parses(expr: str) -> bool:
    try:
        Expression.compile(expr)
    except SyntaxError:
        return False
    return True

Prevention

When it happens

Trigger: Trailing operator with no operand (`-m 'foo and'`), missing closing paren (`-m '(foo'`), empty parens (`-m 'foo()'` with kwargs expected), or duplicated operators (`-m 'foo and and bar'`).

Common situations: Hand-writing marker expressions; partial edits leaving dangling operators; copy-paste that drops the tail of an expression.

Related errors


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