pypa/pip · error · TypeError

Cannot restore Version from {state!r}

Error message

Cannot restore Version from {state!r}

What it means

Raised by Version.__setstate__ when restoring (unpickling/copying) a Version object whose serialized state does not match any of the three supported historical layouts: a 6-tuple (packaging 26.2+), a 2-tuple of (None, slot-dict) (packaging 26.0-26.1), or a plain dict with a '_version' NamedTuple (packaging <=25.x). The library supports cross-version migration of its pickle format, but bails out with a TypeError when the state is structurally unrecognizable. This protects callers from silently constructing a Version with garbage fields.

Source

Thrown at src/pip/_vendor/packaging/version.py:819

                    self._pre = slot_dict.get("_pre")
                    self._post = slot_dict.get("_post")
                    self._dev = slot_dict.get("_dev")
                    self._local = slot_dict.get("_local")
                    return
        if isinstance(state, dict):
            # Old format (packaging <= 25.x, no __slots__): state is a plain
            # dict with "_version" (_Version NamedTuple) and "_key" entries.
            version_nt = state.get("_version")
            if version_nt is not None:
                self._epoch = version_nt.epoch
                self._release = version_nt.release
                self._pre = version_nt.pre
                self._post = version_nt.post
                self._dev = version_nt.dev
                self._local = version_nt.local
                return

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

    @property
    @_deprecated("Version._version is private and will be removed soon")
    def _version(self) -> _Version:
        return _Version(
            self._epoch, self._release, self._dev, self._pre, self._post, self._local
        )

    @_version.setter
    @_deprecated("Version._version is private and will be removed soon")
    def _version(self, value: _Version) -> None:
        self._epoch = value.epoch
        self._release = value.release
        self._dev = value.dev
        self._pre = value.pre
        self._post = value.post
        self._local = value.local
        self._key_cache = None

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Re-create the Version object from its string form on the consuming side: pickle/serialize the str(version) and re-parse with Version(str_version) instead of pickling the object itself.
  2. Align the packaging/pip version on both the producing and consuming sides so the pickle state format matches (the three supported formats span packaging <=25.x, 26.0-26.1, and 26.2+).
  3. If you control the state object, ensure it is a 6-tuple (epoch, release, pre, post, dev, local) matching __getstate__ before calling __setstate__.

Example fix

// before
import pickle
from pip._vendor.packaging.version import Version
blob = pickle.dumps(Version("1.2.3"))  # sent across packaging versions
v = pickle.loads(blob)  # may raise "Cannot restore Version from ..."

// after
v = Version(pickle.loads(blob_str))  # serialize the string, re-parse on read
Defensive patterns

Strategy: try-catch

Validate before calling

import re
from pip._vendor.packaging.version import Version

def is_restorable_version_state(state):
    if isinstance(state, tuple) and len(state) == 6:
        return True
    if isinstance(state, tuple) and len(state) == 2 and isinstance(state[1], dict):
        return all(k in state[1] for k in ('_epoch', '_release'))
    if isinstance(state, dict) and state.get('_version') is not None:
        return True
    return False

def safe_restore(state, fallback_str):
    try:
        v = Version.__new__(Version)
        v.__setstate__(state)
        return v
    except TypeError:
        return Version(fallback_str)

Type guard

# No meaningful runtime type guard for opaque pickle state;
# guard by serializing the *string* form instead of the object.
def version_payload(v):
    return ('version_str', str(v))  # round-trip str, not the object

Try / catch

try:
    v = pickle.loads(blob)
except TypeError as e:
    if 'Cannot restore Version' in str(e):
        raise ValueError('Incompatible packaging pickle; re-create from string') from e
    raise

Prevention

When it happens

Trigger: Unpickling a Version instance whose pickle payload was produced by an incompatible/modified packaging version, a hand-built state object, or data corrupted in transit/storage. copy.deepcopy, multiprocessing, or caching layers (pickle, shelve, redis) that round-trip Version objects across processes running different packaging versions trigger __setstate__.

Common situations: Upgrading or downgrading pip/packaging in a venv while a cached pickle of a Version object (e.g. from pip's resolver cache, a test fixture, or a user app that pickles parsed versions) survives. Also when a custom __reduce__ or external serializer mangles the Version state.

Related errors


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