pypa/pip · error · TypeError
Cannot restore SpecifierSet from
Error message
Cannot restore SpecifierSet from {state!r} What it means
Raised as TypeError by SpecifierSet.__setstate__ (specifiers.py:931) during unpickling when none of the recognized state formats validate. It tries the 26.2+ tuple ((Specifier, ...), prereleases), the 26.0-26.1 slot dict (converting a legacy frozenset of specs to a tuple), and the <=25.x plain dict; failure of all raises this. Like the Specifier case, it signals a packaging-version mismatch in pickle state or corrupt hand-built state.
Solutions
- Store SpecifierSet as str(specifier_set) (round-trippable) rather than pickling the object; reconstruct with SpecifierSet(s) on load.
- Ensure producer and consumer run the same packaging version across the pickle boundary.
- Invalidate and rebuild any cached/pickled SpecifierSet objects after upgrading packaging.
- When loading legacy state, catch TypeError and reconstruct from the string form if available.
Example fix
# before - fragile pickle across packaging versions
import pickle
blob = pickle.dumps(SpecifierSet('>=1.0,!=1.0.1'))
ss = pickle.loads(blob) # TypeError: Cannot restore SpecifierSet
# after - persist the canonical string
blob = str(SpecifierSet('>=1.0,!=1.0.1')).encode()
ss = SpecifierSet(blob.decode()) Defensive patterns
Strategy: fallback
Validate before calling
from packaging.specifiers import SpecifierSet
# Persist and restore the canonical string, not the pickle.
def load_set(s: str) -> 'SpecifierSet':
return SpecifierSet(s) Type guard
from packaging.specifiers import Specifier, SpecifierSet
def is_specifierset_state(state) -> bool:
return (isinstance(state, tuple) and len(state) == 2
and isinstance(state[0], tuple)
and all(isinstance(s, Specifier) for s in state[0])
and (state[1] is None or isinstance(state[1], bool))) Try / catch
import pickle
try:
ss = pickle.loads(blob)
except TypeError:
ss = SpecifierSet(blob.decode()) Prevention
- Store SpecifierSet as its string form for persistence.
- Keep packaging versions aligned across pickle producers and consumers.
- Rebuild caches after upgrading packaging.
When it happens
Trigger: Unpickling a SpecifierSet produced by packaging 26.0 (which stored _specs as a frozenset) in a version that no longer accepts that shape after the frozenset-conversion branch was removed; cross-process pickle transfer between mismatched packaging versions; a spec stored in a frozenset but elements not all Specifier instances.
Common situations: Long-lived persistent requirement cache upgraded across a packaging 25->26 or 26.0->26.2 boundary; CI matrix where one job pickles on packaging 26.1 and another loads on 26.3; vendored pip packaging differing from an application's packaging dependency.
Related errors
- Cannot restore Specifier from
- Cannot restore Requirement from
- Cannot restore Tag from
- Cannot restore Version from
- Cannot combine SpecifierSets with True and False prerelease…
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/b7c76bf3176b5f34.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/specifiers.py:931
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 f399c37189)