{"record":{"id":"be335a8c5ccdb6d9","repo":"pypa/pip","slug":"cannot-restore-tag-from-state-r","errorCode":null,"errorMessage":"Cannot restore Tag from {state!r}","messagePattern":"Cannot restore Tag from (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/tags.py","lineNumber":220,"sourceCode":"        # Cache member _hash is excluded and will be recomputed.\n        return (self._interpreter, self._abi, self._platform)\n\n    def __setstate__(self, state: object) -> None:\n        if isinstance(state, tuple):\n            if len(state) == 3 and all(isinstance(s, str) for s in state):\n                # New format (26.2+): (interpreter, abi, platform)\n                self._interpreter, self._abi, self._platform = state\n                self._hash = hash((self._interpreter, self._abi, self._platform))\n                return\n            if len(state) == 2 and isinstance(state[1], dict):\n                # Old format (packaging <= 26.1, __slots__): (None, {slot: value}).\n                _, slots = state\n                try:\n                    interpreter = slots[\"_interpreter\"]\n                    abi = slots[\"_abi\"]\n                    platform = slots[\"_platform\"]\n                except KeyError:\n                    raise TypeError(f\"Cannot restore Tag from {state!r}\") from None\n                if not all(\n                    isinstance(value, str) for value in (interpreter, abi, platform)\n                ):\n                    raise TypeError(f\"Cannot restore Tag from {state!r}\")\n                self._interpreter = interpreter.lower()\n                self._abi = abi.lower()\n                self._platform = platform.lower()\n                self._hash = hash((self._interpreter, self._abi, self._platform))\n                return\n        raise TypeError(f\"Cannot restore Tag from {state!r}\")\n\n\ndef parse_tag(\n    tag: str, *, validate_order: bool = False, limit: int | None = None\n) -> frozenset[Tag]:\n    \"\"\"\n    Parses the provided tag (e.g. `py3-none-any`) into a frozenset of\n    :class:`Tag` instances.","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/tags.py#L202-L238","documentation":"Raised as TypeError by Tag.__setstate__ (tags.py:220) on the KeyError branch when unpickling a Tag whose pickle uses the legacy <=26.1 slot-dict format (None, {slot: value}) but the dict is missing one of the required keys _interpreter, _abi, or _platform. The except KeyError ... raise TypeError converts the missing-slot case into the standard 'Cannot restore Tag' message.","triggerScenarios":"Unpickling a Tag whose state was tampered with or produced by a custom __reduce__ that omitted a slot; partial/corrupt pickle data; a subclass or proxy that serialized only some slots.","commonSituations":"Corrupted pickle cache of resolved wheel tags; a monkeypatched/custom Tag subclass serialized before the packaging upgrade; cross-process queue corruption; manual pickle manipulation in tests.","solutions":["Do not pickle Tag objects across versions; store the canonical string f'{interpreter}-{abi}-{platform}' and rebuild with Tag(*parts).","Regenerate the tag cache/pickle store from scratch after corruption.","Ensure any custom __reduce__/__getstate__ on a Tag subclass emits all three slot keys.","Catch TypeError on unpickle and fall back to Tag(str_form) parsing."],"exampleFix":"# before - pickle the object\nimport pickle\nblob = pickle.dumps(Tag('cp310','cp310','linux_x86_64'))\ntag = pickle.loads(blob)  # TypeError if slot dict is incomplete\n\n# after - persist the canonical string form\nfrom packaging.tags import Tag\ns = str(Tag('cp310','cp310','linux_x86_64'))  # 'cp310-cp310-linux_x86_64'\ntag = Tag(*s.split('-'))","handlingStrategy":"fallback","validationCode":"# Persist tags as strings, not pickles.\nfrom packaging.tags import Tag\ndef serialize_tag(t: Tag) -> str:\n    return str(t)\ndef deserialize_tag(s: str) -> Tag:\n    return Tag(*s.split('-'))","typeGuard":"from packaging.tags import Tag\ndef has_all_slots(d: dict) -> bool:\n    return all(k in d for k in ('_interpreter', '_abi', '_platform'))","tryCatchPattern":"import pickle\ntry:\n    tag = pickle.loads(blob)\nexcept TypeError:\n    tag = Tag(*blob.decode().split('-'))","preventionTips":["Do not pickle Tag across packaging versions.","Store the canonical 'interp-abi-platform' string and rebuild.","Invalidate corrupt tag caches and regenerate."],"tags":["packaging","tags","pickle","serialization","corruption"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}