pypa/pip · error · InstallationError

Invalid requirement: {req_as_string!r}: {exc}

Error message

Invalid requirement: {req_as_string!r}: {exc}

What it means

Raised by the inner _parse_req_string() in parse_req_from_line() when the requirement string itself (after path/URL/marker handling) cannot be parsed by get_requirement as a valid PEP 508 requirement. The error at constructors.py:405 includes a smart hint: it detects path-like strings, single-`=` mistakes, etc.

Source

Thrown at src/pip/_internal/req/constructors.py:405

        return f"{text} (from {line_source})"

    def _parse_req_string(req_as_string: str) -> Requirement:
        try:
            return get_requirement(req_as_string)
        except InvalidRequirement as exc:
            if os.path.sep in req_as_string:
                add_msg = "It looks like a path."
                add_msg += deduce_helpful_msg(req_as_string)
            elif "=" in req_as_string and not any(
                op in req_as_string for op in operators
            ):
                add_msg = "= is not a valid operator. Did you mean == ?"
            else:
                add_msg = ""
            msg = with_source(f"Invalid requirement: {req_as_string!r}: {exc}")
            if add_msg:
                msg += f"\nHint: {add_msg}"
            raise InstallationError(msg)

    if req_as_string is not None:
        req: Requirement | None = _parse_req_string(req_as_string)
    else:
        req = None

    return RequirementParts(req, link, markers, extras)


def install_req_from_line(
    name: str,
    comes_from: str | InstallRequirement | None = None,
    *,
    isolated: bool = False,
    hash_options: dict[str, list[str]] | None = None,
    constraint: bool = False,
    line_source: str | None = None,
    user_supplied: bool = False,

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Follow the Hint in the error message: if it suggests `==`, replace single `=` with `==`.
  2. If the hint says the argument looks like a requirements file, re-run with `-r <file>`.
  3. Validate the line standalone: `python -c "from packaging.requirements import Requirement; Requirement('YOUR_LINE')"`.
  4. Strip invisible characters and normalize quotes if copy-pasted from a rich-text source.

Example fix

# before
package =1.0
# after
package==1.0
Defensive patterns

Strategy: validation

Validate before calling

from packaging.requirements import Requirement, InvalidRequirement

def validate_req_line(line: str) -> Requirement:
    # strip markers/path handling is pip's job; here we sanity-check the core specifier
    core = line.split(";", 1)[0].strip()
    try:
        return Requirement(core)
    except InvalidRequirement as e:
        raise ValueError(f"Invalid requirement line {line!r}: {e}") from e

Type guard

from packaging.requirements import Requirement, InvalidRequirement

def is_valid_req_line(line: str) -> bool:
    try:
        Requirement(line.split(";", 1)[0].strip())
        return True
    except InvalidRequirement:
        return False

Try / catch

null

Prevention

When it happens

Trigger: A requirements line with bad syntax: `package =1.0` (single equals), an unparseable name, a version specifier with stray characters, or a path-like token that is neither a valid file nor a valid requirement. The hint logic at constructors.py:393-404 appends advice based on heuristics.

Common situations: Using `=` instead of `==`. Forgetting the package name and writing just a version. Stray whitespace or non-ASCII characters. A requirements file accidentally passed without -r.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/835b8ee9c30281c1.json. Report an issue: GitHub.