{"id":"77f530b492ebe1bc","repo":"pypa/pip","slug":"cannot-restore-self-class-name-from-sta","errorCode":null,"errorMessage":"Cannot restore {self.__class__.__name__} from {state!r}","messagePattern":"Cannot restore (.+?) from (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/_parser.py","lineNumber":56,"sourceCode":"            )\n        self.value = value\n\n    def __setstate__(self, state: object) -> None:\n        if isinstance(state, str):\n            # New format (26.2+): just the value string.\n            self._restore_value(state)\n            return\n        if isinstance(state, tuple) and len(state) == 2:\n            # Old format (packaging <= 26.0, __slots__): (None, {slot: value}).\n            _, slot_dict = state\n            if isinstance(slot_dict, dict) and \"value\" in slot_dict:\n                self._restore_value(slot_dict[\"value\"])\n                return\n        if isinstance(state, dict) and \"value\" in state:\n            # Old format (packaging <= 25.0, no __slots__): plain __dict__.\n            self._restore_value(state[\"value\"])\n            return\n        raise TypeError(f\"Cannot restore {self.__class__.__name__} from {state!r}\")\n\n\nclass Variable(Node):\n    __slots__ = ()\n\n    def serialize(self) -> str:\n        return str(self)\n\n\nclass Value(Node):\n    __slots__ = ()\n\n    def serialize(self) -> str:\n        return f'\"{self}\"'\n\n\nclass Op(Node):\n    __slots__ = ()","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/_parser.py#L38-L74","documentation":"Raised by `Node.__setstate__` when the persisted state is neither a plain string (new 26.2+ format), nor the old `(None, {slot: value})` tuple (packaging ≤26.0 with `__slots__`), nor a dict with a `value` key (packaging ≤25.0). It is a backwards-compatibility guard so that pickles produced by older packaging can still be loaded, while genuinely unrecognized formats are rejected.","triggerScenarios":"Unpickling a `Specifier`/`Marker` AST node whose pickle was produced by a much older or future packaging version; a pickle file shared across machines with mismatched packaging versions; a hand-constructed state tuple of the wrong shape.","commonSituations":"Cached build artifacts (pip's wheel cache, build backend caches) persisted on disk and reloaded after a packaging upgrade; copying pickled objects between Python environments; CI using a stale cache volume.","solutions":["Clear the stale cache (pip cache purge, remove `__pycache__`/wheel cache dirs) so objects are re-parsed from source","Pin matching `packaging` versions across the environments exchanging pickles","Serialize the specifier/marker as a string and re-parse via `Specifier()`/`Marker()` on load instead of pickling AST nodes"],"exampleFix":"// before\nobj = pickle.load(open('cache.pkl','rb'))  # raises\n// after\n# do not pickle AST nodes; store the source string\nimport pickle\nfrom packaging.specifiers import Specifier\nsrc = '>=1.0'  # stored in cache instead\nobj = Specifier(src)","handlingStrategy":"try-catch","validationCode":"def is_recognized_node_state(state) -> bool:\n    if isinstance(state, str):\n        return True\n    if isinstance(state, tuple) and len(state) == 2:\n        _, sd = state\n        return isinstance(sd, dict) and 'value' in sd\n    if isinstance(state, dict) and 'value' in state:\n        return True\n    return False","typeGuard":"from typing import Any\ndef is_loadable_node_state(state: Any) -> bool:\n    return (\n        isinstance(state, str)\n        or (isinstance(state, dict) and 'value' in state)\n        or (isinstance(state, tuple) and len(state) == 2\n            and isinstance(state[1], dict) and 'value' in state[1])\n    )","tryCatchPattern":"try:\n    obj = pickle.load(fh)\nexcept TypeError as e:\n    if 'Cannot restore' in str(e):\n        log.warning('stale pickle cache, clearing')\n        os.remove(cache_path)\n        obj = Specifier(src_string)  # re-parse\n    else:\n        raise","preventionTips":["Cache specifier/marker source strings, not AST node pickles","Run `pip cache purge` after upgrading packaging","Version-stamp cache files and invalidate on packaging version mismatch"],"tags":["packaging","parser","pickle","version-compat","cache"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}