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
- Read the ParserSyntaxError detail embedded in the InvalidRequirement message to find the offending position/token.
- Reformat the string to valid PEP 508: 'name[extra] specifier ; marker', e.g. 'requests[security]>=2.31,<3; python_version>="3.8"'.
- If the string comes from a file, trim whitespace/comments and validate line-by-line.
- 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
- Treat user-supplied requirement strings as untrusted input; validate each line.
- Build requirements programmatically from name/specifier/marker components rather than f-strings.
- Strip comments and whitespace before parsing requirements.txt lines.
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
- Invalid quoted string
- Expected end of dependency specifier
- Expected URL after @
- Expected whitespace after URL
- Expected extra name after comma
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/dc296d01e9343e2d.
Report an issue: GitHub.