{"id":"de9a487940d93909","repo":"pytest-dev/pytest","slug":"unexpected-character-input-pos","errorCode":null,"errorMessage":"unexpected character \"{input[pos]}\"","messagePattern":"unexpected character \"(.+?)\"","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/_pytest/mark/expression.py","lineNumber":126,"sourceCode":"                        (FILE_NAME, 1, pos + backslash_pos + 1, input),\n                    )\n                yield Token(TokenType.STRING, value, pos)\n                pos += len(value)\n            else:\n                match = re.match(r\"(:?\\w|:|\\+|-|\\.|\\[|\\]|\\\\|/)+\", input[pos:])\n                if match:\n                    value = match.group(0)\n                    if value == \"or\":\n                        yield Token(TokenType.OR, value, pos)\n                    elif value == \"and\":\n                        yield Token(TokenType.AND, value, pos)\n                    elif value == \"not\":\n                        yield Token(TokenType.NOT, value, pos)\n                    else:\n                        yield Token(TokenType.IDENT, value, pos)\n                    pos += len(value)\n                else:\n                    raise SyntaxError(\n                        f'unexpected character \"{input[pos]}\"',\n                        (FILE_NAME, 1, pos + 1, input),\n                    )\n        yield Token(TokenType.EOF, \"\", pos)\n\n    @overload\n    def accept(self, type: TokenType, *, reject: Literal[True]) -> Token: ...\n\n    @overload\n    def accept(\n        self, type: TokenType, *, reject: Literal[False] = False\n    ) -> Token | None: ...\n\n    def accept(self, type: TokenType, *, reject: bool = False) -> Token | None:\n        if self.current.type is type:\n            token = self.current\n            if token.type is not TokenType.EOF:\n                self.current = next(self.tokens)","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/mark/expression.py#L108-L144","documentation":"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.","triggerScenarios":"Including characters like `@`, `*`, `&`, `!`, `?`, `;` in a `-k`/`-m` expression; using Python operators (`and&&`, `||`) that don't exist in the grammar.","commonSituations":"Confusing pytest's mini-grammar with Python boolean operators; pasting a regex or glob into `-k`; stray shell glob expansions (`*`) reaching pytest.","solutions":["Quote the expression so the shell does not expand `*`/`?`/`!`.","Use only the supported operators `and`, `or`, `not`, parentheses, and identifiers.","For substring matching on test names remember `-k` already does substring match on identifiers — no wildcards needed."],"exampleFix":"# before\npytest -k 'test_* && not slow'\n# after\npytest -k 'test_ and not slow'","handlingStrategy":"validation","validationCode":"from _pytest.mark.expression import Expression\n\ndef only_allowed_chars(expr: str) -> bool:\n    try:\n        Expression.compile(expr)\n    except SyntaxError:\n        return False\n    return True","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Quote the expression so the shell cannot inject `*`, `?`, `!`.","Use only `and`/`or`/`not`/parens/identifiers."],"tags":["pytest","marker-expression","scanner","invalid-character"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}