pypa/pip · error · SyntaxError

invalid requirement: %s

Error message

invalid requirement: %s

What it means

Raised by parse_requirement() when, after parsing the name, extras, and version/URL, there is leftover text whose first character is not ';' (the marker separator). This means the requirement has trailing content that is neither a valid URL, version clause, nor a marker, so it is rejected as an invalid requirement (SyntaxError).

Source

Thrown at src/pip/_vendor/distlib/util.py:253

                remaining = remaining[i + 1:].lstrip()
                # As a special diversion from PEP 508, allow a version number
                # a.b.c in parentheses as a synonym for ~= a.b.c (because this
                # is allowed in earlier PEPs)
                if COMPARE_OP.match(s):
                    versions, _ = get_versions(s)
                else:
                    m = VERSION_IDENTIFIER.match(s)
                    if not m:
                        raise SyntaxError('invalid constraint: %s' % s)
                    v = m.groups()[0]
                    s = s[m.end():].lstrip()
                    if s:
                        raise SyntaxError('invalid constraint: %s' % s)
                    versions = [('~=', v)]

    if remaining:
        if remaining[0] != ';':
            raise SyntaxError('invalid requirement: %s' % remaining)
        remaining = remaining[1:].lstrip()

        mark_expr, remaining = parse_marker(remaining)

    if remaining and remaining[0] != '#':
        raise SyntaxError('unexpected trailing data: %s' % remaining)

    if not versions:
        rs = distname
    else:
        rs = '%s %s' % (distname, ', '.join(['%s %s' % con for con in versions]))
    return Container(name=distname, extras=extras, constraints=versions, marker=mark_expr, url=uri, requirement=rs)


def get_resources_dests(resources_root, rules):
    """Find destinations for resources files"""

    def get_rel_path(root, path):

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Remove the trailing junk shown after 'invalid requirement:'.
  2. If the trailing text is an environment marker, prefix it with '; ': 'pkg >= 1.0 ; python_version >= "3.8"'.
  3. If it is a comment, prefix it with '#'.

Example fix

// before
parse_requirement('requests >= 2.31.0 security')
// after
parse_requirement('requests >= 2.31.0')
Defensive patterns

Strategy: try-catch

Try / catch

from distlib.util import parse_requirement
try:
    spec = parse_requirement(raw)
except SyntaxError as e:
    if 'invalid requirement' in str(e):
        # the whole string is malformed; surface to user / skip line
        continue
    raise

Prevention

When it happens

Trigger: parse_requirement('pkg extra'), parse_requirement('pkg >= 1.0 garbage'), parse_requirement('pkg[extra] something'), or any requirement with unparseable trailing tokens that do not start with ';'.

Common situations: Typos in requirements.txt (e.g. a stray word), comments appended without a '#', or concatenation of a requirement with extra metadata without a separator.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/99ed8217502d0423.json. Report an issue: GitHub.