{"id":"bfafaf863c17acf9","repo":"pytest-dev/pytest","slug":"closing-quote-quote-char-is-missing","errorCode":null,"errorMessage":"closing quote \"{quote_char}\" is missing","messagePattern":"closing quote \"(.+?)\" is missing","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/_pytest/mark/expression.py","lineNumber":100,"sourceCode":"        while pos < len(input):\n            if input[pos] in (\" \", \"\\t\"):\n                pos += 1\n            elif input[pos] == \"(\":\n                yield Token(TokenType.LPAREN, \"(\", pos)\n                pos += 1\n            elif input[pos] == \")\":\n                yield Token(TokenType.RPAREN, \")\", pos)\n                pos += 1\n            elif input[pos] == \"=\":\n                yield Token(TokenType.EQUAL, \"=\", pos)\n                pos += 1\n            elif input[pos] == \",\":\n                yield Token(TokenType.COMMA, \",\", pos)\n                pos += 1\n            elif (quote_char := input[pos]) in (\"'\", '\"'):\n                end_quote_pos = input.find(quote_char, pos + 1)\n                if end_quote_pos == -1:\n                    raise SyntaxError(\n                        f'closing quote \"{quote_char}\" is missing',\n                        (FILE_NAME, 1, pos + 1, input),\n                    )\n                value = input[pos : end_quote_pos + 1]\n                if (backslash_pos := value.find(\"\\\\\")) != -1:\n                    raise SyntaxError(\n                        r'escaping with \"\\\" not supported in marker expression',\n                        (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\":","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/pytest-dev/pytest/blob/98b357f69e380da908740a212288d73b2ee06687/src/_pytest/mark/expression.py#L82-L118","documentation":"Raised by the match-expression scanner when a string literal in marker kwargs starts with a quote but never closes, e.g. `foo(x=\"bar)`. The scanner looks for the matching quote and fails, producing a SyntaxError with the column offset of the opening quote. The expression grammar only allows string values inside `ident(name=value)` kwargs.","triggerScenarios":"Using `-m 'foo(reason=\"missing)'` or a parametrized-style marker call where the closing quote is omitted or mismatched (single vs double).","commonSituations":"Shell quoting eats the closing quote; copy-pasting a marker expression that was split across lines; mismatching `\"` and `'` in kwargs.","solutions":["Match opening and closing quote characters exactly inside kwarg string values.","Single-quote the whole `-m` argument on the shell and use double quotes inside, or vice versa.","Validate the expression in a Python REPL via `_pytest.mark.expression.Expression.compile(...)` before committing it to CI."],"exampleFix":"# before\npytest -m 'slow(reason=\"too slow)'\n# after\npytest -m 'slow(reason=\"too slow\")'","handlingStrategy":"validation","validationCode":"def balanced_quotes(expr: str) -> bool:\n    return expr.count(chr(39)) % 2 == 0 and expr.count(chr(34)) % 2 == 0","typeGuard":null,"tryCatchPattern":"try:\n    Expression.compile(expr)\nexcept SyntaxError as e:\n    if 'closing quote' in e.msg:\n        ...","preventionTips":["Pair every opening quote with the same closing quote character.","Use opposite quote style for the shell vs the literal value."],"tags":["pytest","marker-expression","scanner","quoting"],"analyzedSha":"98b357f69e380da908740a212288d73b2ee06687","analyzedAt":"2026-08-04T20:26:34.442Z","schemaVersion":2}