pypa/pip · error · TypeError
Cannot restore Tag from {state!r}
Error message
Cannot restore Tag from {state!r} What it means
Raised as TypeError (tags.py:190) inside Tag.__setstate__ when unpickling the legacy 26.0-26.1 slot-dict format and the slot dict is missing one of _interpreter, _abi, or _platform (KeyError is caught and re-raised as this TypeError). It signals a corrupt or partial pickle in the old format.
Source
Thrown at src/pip/_vendor/packaging/tags.py:190
# Cache member _hash is excluded and will be recomputed.
return (self._interpreter, self._abi, self._platform)
def __setstate__(self, state: object) -> None:
if isinstance(state, tuple):
if len(state) == 3 and all(isinstance(s, str) for s in state):
# New format (26.2+): (interpreter, abi, platform)
self._interpreter, self._abi, self._platform = state
self._hash = hash((self._interpreter, self._abi, self._platform))
return
if len(state) == 2 and isinstance(state[1], dict):
# Old format (packaging <= 26.1, __slots__): (None, {slot: value}).
_, slots = state
try:
interpreter = slots["_interpreter"]
abi = slots["_abi"]
platform = slots["_platform"]
except KeyError:
raise TypeError(f"Cannot restore Tag from {state!r}") from None
if not all(
isinstance(value, str) for value in (interpreter, abi, platform)
):
raise TypeError(f"Cannot restore Tag from {state!r}")
self._interpreter = interpreter.lower()
self._abi = abi.lower()
self._platform = platform.lower()
self._hash = hash((self._interpreter, self._abi, self._platform))
return
raise TypeError(f"Cannot restore Tag from {state!r}")
def parse_tag(tag: str, *, validate_order: bool = False) -> frozenset[Tag]:
"""
Parses the provided tag (e.g. `py3-none-any`) into a frozenset of
:class:`Tag` instances.
Returning a set is required due to the possibility that the tag is aView on GitHub (pinned to d7d0d0a394)
Solutions
- Discard the corrupt pickle and rebuild the Tag via Tag(interpreter, abi, platform).
- Re-serialize Tags on the current packaging version using the new 3-tuple format.
- Prefer storing tags as their string form ('py3-none-any') and parsing via parse_tag for cross-version durability.
Example fix
# before: old-format pickle missing a slot
tag = pickle.load(open("t.pkl", "rb")) # TypeError
# after
from pip._vendor.packaging.tags import Tag
tag = Tag("cp312", "cp312", "manylinux_2_17_x86_64") Defensive patterns
Strategy: try-catch
Validate before calling
def is_intact_legacy_tag_state(state):
if (isinstance(state, tuple) and len(state) == 2 and isinstance(state[1], dict)):
slots = state[1]
return all(k in slots for k in ('_interpreter', '_abi', '_platform'))
return False Type guard
null
Try / catch
import pickle
from pip._vendor.packaging.tags import Tag
try:
t = pickle.load(open(path, 'rb'))
except TypeError as e:
if 'Cannot restore Tag' in str(e):
t = Tag(interp_str, abi_str, platform_str) Prevention
- Re-pickle Tags on the current packaging so they use the new 3-tuple format.
- Store tags as 'interpreter-abi-platform' strings for durability.
When it happens
Trigger: pickle.load on a Tag pickled by packaging 26.0-26.1 whose (None, {...}) state dict lacks one of the three slot keys. The except KeyError branch at tags.py:189 converts the lookup failure into this explicit restore error.
Common situations: Truncated pickle bytes, a pickle from a Tag subclass that overrode __slots__, or manual mutation of the state dict before unpickling.
Related errors
- Cannot restore {self.__class__.__name__} value from {value!r
- Cannot restore Requirement from {state!r}
- Cannot restore Specifier from {state!r}
- Cannot restore SpecifierSet from {state!r}
- Cannot restore {self.__class__.__name__} from {state!r}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/be335a8c5ccdb6d9.json.
Report an issue: GitHub.