{"id":"8a6fa6e008db6744","repo":"pypa/pip","slug":"invalid-constraint-s","errorCode":null,"errorMessage":"invalid constraint: %s","messagePattern":"invalid constraint: (.+?)","errorType":"validation","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/distlib/util.py","lineNumber":223,"sourceCode":"                    while True:\n                        op = m.groups()[0]\n                        ver_remaining = ver_remaining[m.end():]\n                        m = VERSION_IDENTIFIER.match(ver_remaining)\n                        if not m:\n                            raise SyntaxError('invalid version: %s' % ver_remaining)\n                        v = m.groups()[0]\n                        versions.append((op, v))\n                        ver_remaining = ver_remaining[m.end():]\n                        if not ver_remaining or ver_remaining[0] != ',':\n                            break\n                        ver_remaining = ver_remaining[1:].lstrip()\n                        # Some packages have a trailing comma which would break things\n                        # See issue #148\n                        if not ver_remaining:\n                            break\n                        m = COMPARE_OP.match(ver_remaining)\n                        if not m:\n                            raise SyntaxError('invalid constraint: %s' % ver_remaining)\n                    if not versions:\n                        versions = None\n                return versions, ver_remaining\n\n            if remaining[0] != '(':\n                versions, remaining = get_versions(remaining)\n            else:\n                i = remaining.find(')', 1)\n                if i < 0:\n                    raise SyntaxError('unterminated parenthesis: %s' % remaining)\n                s = remaining[1:i]\n                remaining = remaining[i + 1:].lstrip()\n                # As a special diversion from PEP 508, allow a version number\n                # a.b.c in parentheses as a synonym for ~= a.b.c (because this\n                # is allowed in earlier PEPs)\n                if COMPARE_OP.match(s):\n                    versions, _ = get_versions(s)\n                else:","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/distlib/util.py#L205-L241","documentation":"Raised by get_versions() inside parse_requirement() after a comma separating multiple version constraints. Once a comma is consumed, the next token must begin with a comparison operator (matched by COMPARE_OP); if it does not, the constraint list is considered invalid and a SyntaxError is raised. It guards the 'op ver, op ver' chaining syntax of PEP 508 version specifiers.","triggerScenarios":"parse_requirement('pkg >= 1.0 2.0'), parse_requirement('pkg >= 1.0, 2.0'), or any specifier list where a comma-separated element omits the leading operator or contains junk between constraints.","commonSituations":"Authors writing 'pkg >= 1.0, 2.0' (forgetting the second operator), or automated tools that join version filters with commas without re-emitting operators.","solutions":["Repeat the operator before each comma-separated version: 'pkg >= 1.0, < 2.0'.","Check the substring after 'invalid constraint:' to find the offending token and fix or remove it.","When constructing compound constraints programmatically, always emit '<op> <ver>' pairs joined by ', '."],"exampleFix":"// before\nparse_requirement('django >= 4.0, 5.0')\n// after\nparse_requirement('django >= 4.0, < 5.0')","handlingStrategy":"validation","validationCode":"import re\ndef validate_compound_constraint(spec):\n    # ensure each comma-separated piece starts with a compare operator\n    body = spec.split(';', 1)[0]\n    m = re.search(r'\\((.*)\\)', body)\n    if not m:\n        return True\n    for piece in m.group(1).split(','):\n        if not re.match(r'\\s*(==|!=|<=|>=|~=|<|>)\\s*\\S', piece):\n            return False\n    return True","typeGuard":null,"tryCatchPattern":"from distlib.util import parse_requirement\ntry:\n    parse_requirement(line)\nexcept SyntaxError as e:\n    if 'invalid constraint' in str(e):\n        reject_line(line, e)\n    else:\n        raise","preventionTips":["Always emit an operator before each comma-separated version.","Validate compound constraints with a regex before parsing.","Generate requirements from dependency data structures, not string templates."],"tags":["requirement","parsing","pep508","packaging","validation"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}