pypa/pip · error · TypeError

Cannot restore Requirement from {state!r}

Error message

Cannot restore Requirement from {state!r}

What it means

Raised as TypeError (requirements.py:97) inside Requirement.__setstate__ when unpickling a Requirement whose stored state is the new-format string (packaging >=26.2) but that string no longer parses via Requirement(). The constructor failure (InvalidRequirement) is re-raised as this TypeError so the pickle layer reports an unreadable pickle.

Source

Thrown at src/pip/_vendor/packaging/requirements.py:97

            yield f" @ {self.url}"
            if self.marker:
                yield " "

        if self.marker:
            yield f"; {self.marker}"

    def __getstate__(self) -> str:
        # Return the requirement string for compactness and stability.
        # Re-parsed on load to reconstruct all fields.
        return str(self)

    def __setstate__(self, state: object) -> None:
        if isinstance(state, str):
            # New format (26.2+): just the requirement string.
            try:
                tmp = Requirement(state)
            except InvalidRequirement as exc:
                raise TypeError(f"Cannot restore Requirement from {state!r}") from exc
            self.name = tmp.name
            self.url = tmp.url
            self.extras = tmp.extras
            self.specifier = tmp.specifier
            self.marker = tmp.marker
            return
        if isinstance(state, dict):
            # Old format (packaging <= 26.1, no __slots__): plain __dict__.
            self.__dict__.update(state)
            return
        raise TypeError(f"Cannot restore Requirement from {state!r}")

    def __str__(self) -> str:
        return "".join(self._iter_parts(self.name))

    def __repr__(self) -> str:
        return f"<{self.__class__.__name__}({str(self)!r})>"

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Discard the stale pickle/cache and rebuild the Requirement from the original requirement string.
  2. Pin both producer and consumer to the same packaging version.
  3. Catch TypeError around pickle.load and fall back to re-parsing Requirement(str(...)) from a trusted source.

Example fix

# before
req = pickle.load(open("cache.pkl", "rb"))  # raises TypeError

# after
try:
    req = pickle.load(open("cache.pkl", "rb"))
except TypeError:
    from pip._vendor.packaging.requirements import Requirement
    req = Requirement(open("req.txt").read().strip())
Defensive patterns

Strategy: try-catch

Validate before calling

from pip._vendor.packaging.requirements import Requirement, InvalidRequirement
def requirement_string_restorable(req_string):
    try:
        Requirement(req_string)
        return True
    except InvalidRequirement:
        return False

Type guard

null

Try / catch

import pickle
from pip._vendor.packaging.requirements import Requirement
try:
    obj = pickle.load(open(path, 'rb'))
except TypeError as e:
    if 'Cannot restore Requirement' in str(e):
        obj = Requirement(trusted_requirement_string())

Prevention

When it happens

Trigger: pickle.load() on a pickle produced by packaging >=26.2 where the requirement string has since become invalid (parser regression, manual edit, cross-vendor parser differences). The new __getstate__ returns str(self), so any corruption of that string trips this path.

Common situations: Loading a cached pickle (e.g. a resolved requirements cache) after upgrading packaging in a way that tightened the PEP 508 grammar. Cross-process handoff where the producer and consumer disagree on requirement-string validity.

Related errors


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