pypa/pip · error · SyntaxError
invalid requirement
Error message
invalid requirement: %s
What it means
Raised as SyntaxError inside parse_requirement when, after consuming the distribution name, extras, URL and version constraints, there is leftover text that does not begin with ';' (the PEP 508 marker separator). It signals that the requirement contains unrecognized trailing structure that is neither a version constraint nor a valid marker clause. The parser expects either an empty remainder or a ';' introducing an environment marker.
Solutions
- Inspect the reported trailing fragment and remove or reformat it (e.g. add a missing comparison operator).
- Use packaging.requirements.Requirement(req_str) to validate the string and surface a clearer PEP 508 diagnostic first.
- Rebuild the requirement from a known-good template rather than string concatenation.
Example fix
// before
parse_requirement('requests 2.25.1 extras')
// after
parse_requirement('requests >= 2.25.1') Defensive patterns
Strategy: validation
Validate before calling
from packaging.requirements import Requirement
def safe_parse(req):
Requirement(req) # raises clear PEP 508 error first
return parse_requirement(req) Type guard
def looks_like_valid_requirement(s: str) -> bool:
try:
Requirement(s)
return True
except Exception:
return False Try / catch
try:
parse_requirement(req)
except SyntaxError as e:
if 'invalid requirement' in str(e):
log_and_skip(req, e) Prevention
- Pre-validate with packaging.requirements.Requirement.
- Never build requirements by naive string concatenation.
When it happens
Trigger: parse_requirement('pkg >=1.0 extra_stuff'), parse_requirement('pkg[extra] 1.0'), or any requirement string with a stray token where a comparison operator or ';' is expected. Also produced by requirement strings that accidentally include a second bare version after the first constraint.
Common situations: Hand-constructed requirement strings with typos; concatenating name + version without an operator; metadata from non-conformant packages; build tools joining fragments incorrectly.
Related errors
- unexpected trailing data
- Invalid requirement
- Invalid requirement
- Expected end of dependency specifier
- Expected end of marker expression
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/99ed8217502d0423.
Report an issue: GitHub.
Appendix: 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 f399c37189)