pypa/pip · error · InvalidRequirement

{str(e)}

Error message

{str(e)}

What it means

InvalidRequirement raised by Requirement.__init__ when the PEP 508 requirement string fails tokenizing/parsing (a ParserSyntaxError from the parser). This covers malformed name, extras, URL, or marker syntax — anything the grammar rejects before specifier validation even begins.

Source

Thrown at src/pip/_vendor/packaging/requirements.py:86

        override; it is now included again.

        Equality and hashing normalize requirement names, extras, and
        equivalent specifiers. The string representation still preserves the
        parsed name and extras spelling.
    """

    # TODO: Can we test whether something is contained within a requirement?
    #       If so how do we do that? Do we need to test against the _name_ of
    #       the thing as well as the version? What about the markers?
    # TODO: Can we normalize the name and extra name?

    __slots__ = ("extras", "marker", "name", "specifier", "url")

    def __init__(self, requirement_string: str) -> None:
        try:
            parsed = _parse_requirement(requirement_string)
        except ParserSyntaxError as e:
            raise InvalidRequirement(str(e)) from e

        self.name: str = parsed.name
        self.url: str | None = parsed.url or None
        self.extras: set[str] = set(parsed.extras)
        try:
            self.specifier: SpecifierSet = SpecifierSet(parsed.specifier)
        except InvalidSpecifier as e:
            raise InvalidRequirement(str(e)) from e
        self.marker: Marker | None = None
        if parsed.marker is not None:
            self.marker = Marker.__new__(Marker)
            self.marker._markers = _normalize_extra_values(parsed.marker)

    def _iter_parts(self, name: str) -> Iterator[str]:
        yield name

        if self.extras:
            formatted_extras = ",".join(sorted(self.extras))

View on GitHub (pinned to f399c37189)

Solutions

  1. Read the ParserSyntaxError detail embedded in the InvalidRequirement message to find the offending position/token.
  2. Reformat the string to valid PEP 508: 'name[extra] specifier ; marker', e.g. 'requests[security]>=2.31,<3; python_version>="3.8"'.
  3. If the string comes from a file, trim whitespace/comments and validate line-by-line.
  4. For programmatic construction, build name, specifier, and marker separately and join with the documented operators.

Example fix

# before
Requirement('requests security>=2.31')  # InvalidRequirement

# after
Requirement('requests[security]>=2.31')
Defensive patterns

Strategy: try-catch

Validate before calling

from packaging.requirements import Requirement, InvalidRequirement

def parse_requirement_safe(s: str):
    try:
        return Requirement(s), None
    except InvalidRequirement as e:
        return None, str(e)

req, err = parse_requirement_safe(line)
if err:
    # report the offending line and skip; do not crash the whole import
    ...

Type guard

null

Try / catch

from packaging.requirements import InvalidRequirement
for line in lines:
    try:
        req = Requirement(line)
    except InvalidRequirement as e:
        log.warning("skipping invalid requirement %r: %s", line, e)
        continue
    requirements.append(req)

Prevention

When it happens

Trigger: Calling Requirement('foo >=1.0') is fine, but Requirement('foo bar') (illegal token), Requirement('[extra]foo') (extras before name), Requirement('foo @') (incomplete URL), or Requirement('foo;') (empty marker) raise from _parse_requirement.

Common situations: User-supplied requirements.txt lines; a dependency string built by string concatenation that drops a needed operator; copy-paste of a PEP 508 string with a stray character; a marker missing its value.

Related errors


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