pypa/pip · error · ParserSyntaxError

Invalid quoted string

Error message

Invalid quoted string

What it means

When parsing a PEP 508 environment marker, _parse_marker_var encounters a QUOTED_STRING token and processes it via process_python_str, which calls ast.literal_eval. If the string cannot be safely evaluated (malformed quotes, invalid escape sequences), SyntaxError or ValueError is caught and ParserSyntaxError('Invalid quoted string') is raised with a span pointing at the offending token.

Source

Thrown at src/pip/_vendor/packaging/_parser.py:378

    marker_op = _parse_marker_op(tokenizer)
    tokenizer.consume("WS")
    marker_var_right = _parse_marker_var(tokenizer)
    tokenizer.consume("WS")
    return (marker_var_left, marker_op, marker_var_right)


def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar:  # noqa: RET503
    """
    marker_var = VARIABLE | QUOTED_STRING
    """
    if tokenizer.check("VARIABLE"):
        return process_env_var(tokenizer.read().text.replace(".", "_"))
    elif tokenizer.check("QUOTED_STRING"):
        token = tokenizer.read()
        try:
            return process_python_str(token.text)
        except (SyntaxError, ValueError) as exc:
            raise ParserSyntaxError(
                "Invalid quoted string",
                source=tokenizer.source,
                span=(token.position, token.position + len(token.text)),
            ) from exc
    else:
        tokenizer.raise_syntax_error(
            message="Expected a marker variable or quoted string"
        )


def process_env_var(env_var: str) -> Variable:
    if env_var in ("platform_python_implementation", "python_implementation"):
        return Variable("platform_python_implementation")
    else:
        return Variable(env_var)


def process_python_str(python_str: str) -> Value:

View on GitHub (pinned to f399c37189)

Solutions

  1. Fix the quoting in the marker expression so every quoted string has matching opening/closing quotes of the same type
  2. Use consistent quote style (single quotes) throughout marker expressions
  3. Validate requirement strings with Requirement() early during config loading to surface errors immediately

Example fix

# before
from packaging.requirements import Requirement
req = Requirement('django>=4.0 ; python_version >= "3.8')  # unclosed quote

# after
req = Requirement("django>=4.0 ; python_version >= '3.8'")  # properly quoted
Defensive patterns

Strategy: try-catch

Validate before calling

def validate_marker_quoting(marker_str: str) -> bool:
    for quote_char in ('"', "'"):
        if marker_str.count(quote_char) % 2 != 0:
            return False
    return True

Try / catch

from packaging.requirements import Requirement, InvalidRequirement
from packaging._tokenizer import ParserSyntaxError

try:
    req = Requirement(req_string)
except (InvalidRequirement, ParserSyntaxError) as e:
    print(f"Invalid requirement syntax: {e}")

Prevention

When it happens

Trigger: Calling parse_marker(), parse_requirement(), or constructing Requirement/Marker objects with a specifier containing a malformed quoted string in an environment marker, e.g. python_version >= "3.8 (missing closing quote).

Common situations: Typos in requirements.txt or pyproject.toml markers. Programmatically building requirement strings with unescaped or mismatched quotes. Copy-paste errors in dependency specifiers.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/e0635fc69f0e31b4. Report an issue: GitHub.