pytest-dev/pytest · error · SyntaxError

unexpected character "{input[pos]}"

Error message

unexpected character "{input[pos]}"

What it means

Raised by the scanner when the current character is not whitespace, not one of `()` `=` `,` quotes, and does not begin a valid identifier token (the regex `(:?\w|:|\+|-|\.|\[|\]|\\|/)+`). It reports the offending character and its 1-based column.

Source

Thrown at src/_pytest/mark/expression.py:126

                        (FILE_NAME, 1, pos + backslash_pos + 1, input),
                    )
                yield Token(TokenType.STRING, value, pos)
                pos += len(value)
            else:
                match = re.match(r"(:?\w|:|\+|-|\.|\[|\]|\\|/)+", input[pos:])
                if match:
                    value = match.group(0)
                    if value == "or":
                        yield Token(TokenType.OR, value, pos)
                    elif value == "and":
                        yield Token(TokenType.AND, value, pos)
                    elif value == "not":
                        yield Token(TokenType.NOT, value, pos)
                    else:
                        yield Token(TokenType.IDENT, value, pos)
                    pos += len(value)
                else:
                    raise SyntaxError(
                        f'unexpected character "{input[pos]}"',
                        (FILE_NAME, 1, pos + 1, input),
                    )
        yield Token(TokenType.EOF, "", pos)

    @overload
    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)

View on GitHub (pinned to 98b357f69e)

Solutions

  1. Quote the expression so the shell does not expand `*`/`?`/`!`.
  2. Use only the supported operators `and`, `or`, `not`, parentheses, and identifiers.
  3. For substring matching on test names remember `-k` already does substring match on identifiers — no wildcards needed.

Example fix

# before
pytest -k 'test_* && not slow'
# after
pytest -k 'test_ and not slow'
Defensive patterns

Strategy: validation

Validate before calling

from _pytest.mark.expression import Expression

def only_allowed_chars(expr: str) -> bool:
    try:
        Expression.compile(expr)
    except SyntaxError:
        return False
    return True

Prevention

When it happens

Trigger: Including characters like `@`, `*`, `&`, `!`, `?`, `;` in a `-k`/`-m` expression; using Python operators (`and&&`, `||`) that don't exist in the grammar.

Common situations: Confusing pytest's mini-grammar with Python boolean operators; pasting a regex or glob into `-k`; stray shell glob expansions (`*`) reaching pytest.

Related errors


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