pypa/pip · error · TypeError

Cannot restore SpecifierSet from {state!r}

Error message

Cannot restore SpecifierSet from {state!r}

What it means

Raised as TypeError (specifiers.py:1502) inside SpecifierSet.__setstate__ when the pickled state is unrecognized: it accepts the new (specs_tuple, prereleases) format, the 26.0-26.1 slot-dict tuple, or the legacy plain dict, all requiring specs to be a tuple of Specifier and prereleases to pass _validate_pre. Any other shape hits this fall-through.

Source

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

        if isinstance(state, dict):
            # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
            specs = state.get("_specs", ())
            prereleases = state.get("_prereleases")
            # Convert frozenset to tuple (26.0 stored as frozenset)
            if isinstance(specs, frozenset):
                specs = tuple(sorted(specs, key=str))
            if (
                isinstance(specs, tuple)
                and all(isinstance(s, Specifier) for s in specs)
                and _validate_pre(prereleases)
            ):
                self._specs = specs
                self._prereleases = prereleases
                self._canonicalized = len(self._specs) <= 1
                self._has_arbitrary = any("===" in str(s) for s in self._specs)
                return

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

    def __repr__(self) -> str:
        """A representation of the specifier set that shows all internal state.

        Note that the ordering of the individual specifiers within the set may not
        match the input string.

        >>> SpecifierSet('>=1.0.0,!=2.0.0')
        <SpecifierSet('!=2.0.0,>=1.0.0')>
        >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False)
        <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)>
        >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True)
        <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)>
        """
        pre = (
            f", prereleases={self.prereleases!r}"
            if self._prereleases is not None
            else ""

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Rebuild the SpecifierSet from its string form: SpecifierSet(str(pickle.load(...))) is unsafe if load itself fails; instead store/restore the string representation.
  2. Pin producer and consumer to compatible packaging versions.
  3. Prefer serializing SpecifierSet as str(specifier_set) rather than as a pickle.

Example fix

# before: pickle shared across incompatible packaging versions
ss = pickle.load(open("ss.pkl", "rb"))  # TypeError

# after: persist the round-trippable string instead
# write: open("ss.txt","w").write(str(ss))
from pip._vendor.packaging.specifiers import SpecifierSet
ss = SpecifierSet(open("ss.txt").read().strip())
Defensive patterns

Strategy: try-catch

Validate before calling

def is_recognized_specifierset_state(state):
    if isinstance(state, tuple) and len(state) == 2:
        specs, pre = state
        from pip._vendor.packaging.specifiers import Specifier
        return (isinstance(specs, tuple) and all(isinstance(s, Specifier) for s in specs))
    if isinstance(state, dict):
        return True
    return False

Type guard

null

Try / catch

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

Prevention

When it happens

Trigger: Unpickling a SpecifierSet whose state was produced by an incompatible packaging, manually edited, or whose specs member is not a tuple of Specifier (e.g. a list, or contains non-Specifier objects). Also if prereleases is a non-bool/non-None value.

Common situations: Cross-release pickle caches (resolution caches, lock caches) shared between packaging versions. A pickle from a fork that changed SpecifierSet internals. frozenset specs are tolerated but lists or other iterables are not.

Related errors


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