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

  1. Quote all marker values: use python_version > "3.8" not python_version > 3.8
  2. Verify marker variable names against PEP 508 (e.g. python_version, sys_platform, platform_machine)
  3. Test the marker in isolation: python -c "from packaging.markers import Marker; Marker('python_version > \"3.8\"')"
  4. 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

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


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)