pypa/pip · error · TypeError
Cannot restore Specifier from
Error message
Cannot restore Specifier from {state!r} What it means
Raised as TypeError by Specifier.__setstate__ (specifiers.py:502) during unpickling when the saved pickle state does not match any recognized layout. __setstate__ tries three formats in order: the 26.2+ tuple ((operator, version), prereleases), the 26.0-26.1 slot dict (None, {slot: value}), and the <=25.x plain dict; if _validate_spec/_validate_pre reject all three, this is raised. It indicates a pickle produced by an incompatible packaging version or hand-crafted/corrupt state.
Solutions
- Re-serialize the Specifier by storing str(spec) (which round-trips) instead of pickling the object, and reconstruct via Specifier(str_repr) on load.
- Align packaging versions on both sides of the pickle boundary (producer and consumer) so the state layout matches.
- Clear/flush any persistent pickle cache (disk cache, memcached, multiprocessing queues) after upgrading packaging.
- If you must pickle, catch TypeError in __setstate__ consumers and fall back to re-creating from the string form.
Example fix
# before - pickle the object directly (fragile across versions)
import pickle
blob = pickle.dumps(Specifier('>=1.0.0'))
spec = pickle.loads(blob) # TypeError after a packaging upgrade
# after - persist the round-trippable string
from packaging.specifiers import Specifier
blob = str(Specifier('>=1.0.0')).encode() # b'>=1.0.0'
spec = Specifier(blob.decode()) Defensive patterns
Strategy: fallback
Validate before calling
# Before unpickling, prefer the round-trippable string form.
# If you have a string, never pickle the object:
from packaging.specifiers import Specifier
def load_specifier(s: str) -> 'Specifier':
return Specifier(s) Type guard
from packaging.specifiers import Specifier
def is_specifier_state(state) -> bool:
return (isinstance(state, tuple) and len(state) == 2
and isinstance(state[0], tuple) and len(state[0]) == 2
and all(isinstance(x, str) for x in state[0])
and (state[1] is None or isinstance(state[1], bool))) Try / catch
import pickle
try:
spec = pickle.loads(blob)
except TypeError:
spec = Specifier(blob.decode()) # fallback if blob is the string form Prevention
- Never pickle Specifier across packaging versions; persist str(spec) instead.
- Align packaging versions across all processes that exchange pickles.
- Flush pickle caches after any packaging upgrade.
When it happens
Trigger: pickle.loads() of a Specifier pickled under packaging <=25.x being loaded under 26.2+ where the dict no longer carries the expected keys (or vice versa); copying a Specifier through a multiprocessing queue between processes with different packaging versions; constructing a fake pickle payload with a malformed state tuple.
Common situations: Cross-version deployment: a worker process on packaging 24.x writes Specifier pickles to a cache/queue, a consumer on 26.x reads them and the layout has shifted; caching resolved specifiers in a persistent store across a packaging upgrade; environments with vendored vs. system packaging differing.
Related errors
- Cannot restore SpecifierSet 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/e12bb059e8493c28.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/specifiers.py:502
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 f399c37189)