pypa/pip · error · TypeError

Cannot restore Specifier from {state!r}

Error message

Cannot restore Specifier from {state!r}

What it means

Raised as TypeError (specifiers.py:802) inside Specifier.__setstate__ when the pickled state matches none of the recognized formats: new tuple ((operator, version), prereleases), the 26.0-26.1 slot-dict tuple, or the legacy plain dict. It is the fail-closed branch for corrupt or foreign Specifier pickles.

Source

Thrown at src/pip/_vendor/packaging/specifiers.py:802

            if len(state) == 2 and isinstance(state[1], dict):
                # Format (packaging 26.0-26.1): (None, {slot: value}).
                _, slot_dict = state
                spec = slot_dict.get("_spec")
                prereleases = slot_dict.get("_prereleases", "invalid")
                if _validate_spec(spec) and _validate_pre(prereleases):
                    self._spec = spec
                    self._prereleases = prereleases
                    return
        if isinstance(state, dict):
            # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
            spec = state.get("_spec")
            prereleases = state.get("_prereleases", "invalid")
            if _validate_spec(spec) and _validate_pre(prereleases):
                self._spec = spec
                self._prereleases = prereleases
                return

        raise TypeError(f"Cannot restore Specifier from {state!r}")

    @property
    def operator(self) -> str:
        """The operator of this specifier.

        >>> Specifier("==1.2.3").operator
        '=='
        """
        return self._spec[0]

    @property
    def version(self) -> str:
        """The version of this specifier.

        >>> Specifier("==1.2.3").version
        '1.2.3'
        """
        return self._spec[1]

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Discard the pickle and reconstruct the Specifier from its string form via Specifier(str(...)).
  2. Align producer and consumer packaging versions so __getstate__/__setstate__ agree.
  3. Avoid pickling Specifier across processes with different vendored packaging copies.

Example fix

# before
spec = pickle.load(open("s.pkl", "rb"))  # TypeError

# after
try:
    spec = pickle.load(open("s.pkl", "rb"))
except TypeError:
    from pip._vendor.packaging.specifiers import Specifier
    spec = Specifier(open("spec.txt").read().strip())
Defensive patterns

Strategy: try-catch

Validate before calling

from typing import Any
def is_recognized_specifier_state(state):
    # new: ((op, ver), prereleases) ; legacy slot: (None, dict) ; legacy: dict
    if isinstance(state, tuple) and len(state) == 2:
        return True
    if isinstance(state, dict):
        return True
    return False

Type guard

null

Try / catch

import pickle
from pip._vendor.packaging.specifiers import Specifier
try:
    spec = pickle.load(open(path, 'rb'))
except TypeError as e:
    if 'Cannot restore Specifier' in str(e):
        spec = Specifier(open('spec.txt').read().strip())

Prevention

When it happens

Trigger: pickle.load on a Specifier whose state has been altered, truncated, or produced by a non-standard __getstate__. Reached only after all three format-detection branches decline the state.

Common situations: Cross-version pickle sharing where the producer's packaging is neither the new 26.2+ format nor one of the recognized legacy ones (e.g. a custom subclass). Manual tampering with pickle bytes. A pickle from a different Specifier implementation.

Related errors


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