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

  1. Re-serialize the Specifier by storing str(spec) (which round-trips) instead of pickling the object, and reconstruct via Specifier(str_repr) on load.
  2. Align packaging versions on both sides of the pickle boundary (producer and consumer) so the state layout matches.
  3. Clear/flush any persistent pickle cache (disk cache, memcached, multiprocessing queues) after upgrading packaging.
  4. 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

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


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)