pypa/pip · error · InstallationError
Invalid requirement
Error message
Invalid requirement: {name.strip()!r}: {exc} What it means
While parsing a requirement from a line, pip successfully split off the marker portion (after ';') but failed to construct a packaging Marker object from it. The marker must follow PEP 508 environment marker syntax, and an InvalidMarker exception was caught and re-raised as an InstallationError.
Solutions
- Quote all marker values: use python_version > "3.8" not python_version > 3.8
- Verify marker variable names against PEP 508 (e.g. python_version, sys_platform, platform_machine)
- Test the marker in isolation: python -c "from packaging.markers import Marker; Marker('python_version > \"3.8\"')"
- Check for smart/curly quotes introduced by copy-paste and replace with straight quotes
Example fix
# before package ; python_version > 3.8 # after package ; python_version > "3.8"
Defensive patterns
Strategy: validation
Validate before calling
from pip._vendor.packaging.markers import Marker, InvalidMarker
def validate_marker(marker_str: str) -> bool:
try:
Marker(marker_str)
return True
except InvalidMarker:
return False
if ";" in req_line:
_, marker_part = req_line.split(";", 1)
if not validate_marker(marker_part.strip()):
print(f"Invalid marker: {marker_part!r}") Type guard
from pip._vendor.packaging.markers import Marker, InvalidMarker
def is_valid_marker(marker_str: str) -> bool:
"""Type guard: True if marker_str is a valid PEP 508 marker."""
try:
Marker(marker_str)
return True
except InvalidMarker:
return False Try / catch
from pip._internal.exceptions import InstallationError
try:
parts = parse_req_from_line(line, source)
except InstallationError as e:
if "Invalid requirement" in str(e):
# log and skip or fix the marker
logger.error("Skipping line with bad marker: %s", line)
else:
raise Prevention
- Always quote marker values: python_version > "3.8" not python_version > 3.8
- Validate markers with packaging.markers.Marker() before adding them to requirements files
- Beware copy-paste from rendered documentation that converts straight quotes to smart quotes
When it happens
Trigger: A requirement line with a malformed environment marker such as 'package ; python_version > 3.8' (missing quotes around the value) or 'package ; bogus_marker == x'. The marker string is passed to packaging.markers.Marker() which rejects it.
Common situations: Using unquoted marker values (python_version > 3.8 instead of python_version > "3.8"). Typo in marker variable names. Copy-pasting from rendered docs that converted straight quotes to smart quotes. Using markers unsupported by the installed packaging library version.
Related errors
- Invalid requirement
- Invalid requirement
- invalid requirement
- unexpected trailing data
- Configuration file could not be loaded.\n
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/530bddd32a9b0b5c.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_internal/req/constructors.py:349
)
return path_to_url(path)
def parse_req_from_line(name: str, line_source: str | None) -> RequirementParts:
if is_url(name):
marker_sep = "; "
else:
marker_sep = ";"
if marker_sep in name:
name, markers_as_string = name.split(marker_sep, 1)
markers_as_string = markers_as_string.strip()
if not markers_as_string:
markers = None
else:
try:
markers = Marker(markers_as_string)
except InvalidMarker as exc:
raise InstallationError(f"Invalid requirement: {name.strip()!r}: {exc}")
else:
markers = None
name = name.strip()
req_as_string = None
path = os.path.normpath(os.path.abspath(name))
link = None
if is_url(name):
link = Link(name)
extras: set[str] = set()
else:
p, extras = strip_extras(path)
url = _get_url_from_path(p, name)
if url is not None:
link = Link(url)
# it's a local file, dir, or url
if link:View on GitHub (pinned to f399c37189)