pypa/pip · error · TypeError
Cannot restore {self.__class__.__name__} value from {value!r
Error message
Cannot restore {self.__class__.__name__} value from {value!r} What it means
Raised by `packaging._parser.Node._restore_value` during unpickling/deepcopy when the persisted value is not a `str`. AST nodes (`Variable`, `Value`, `Op`, etc.) carry a single string `value`; any non-string passed to restore is rejected as a type error. This guard enforces the new compact pickle format introduced in packaging 26.2.
Source
Thrown at src/pip/_vendor/packaging/_parser.py:36
def __init__(self, value: str) -> None:
self.value = value
def __str__(self) -> str:
return self.value
def __repr__(self) -> str:
return f"<{self.__class__.__name__}({self.value!r})>"
def serialize(self) -> str:
raise NotImplementedError
def __getstate__(self) -> str:
# Return just the value string for compactness and stability.
return self.value
def _restore_value(self, value: object) -> None:
if not isinstance(value, str):
raise TypeError(
f"Cannot restore {self.__class__.__name__} value from {value!r}"
)
self.value = value
def __setstate__(self, state: object) -> None:
if isinstance(state, str):
# New format (26.2+): just the value string.
self._restore_value(state)
return
if isinstance(state, tuple) and len(state) == 2:
# Old format (packaging <= 26.0, __slots__): (None, {slot: value}).
_, slot_dict = state
if isinstance(slot_dict, dict) and "value" in slot_dict:
self._restore_value(slot_dict["value"])
return
if isinstance(state, dict) and "value" in state:
# Old format (packaging <= 25.0, no __slots__): plain __dict__.
self._restore_value(state["value"])View on GitHub (pinned to d7d0d0a394)
Solutions
- Ensure the object being unpickled was created by the same packaging version — re-parse from the source string instead
- If you must rebuild state, pass a str: `node.__setstate__('>=1.0')`
- Avoid pickling marker/specifier AST nodes; serialize the source specifier string and re-parse on load
Example fix
// before
node.__setstate__(123)
// after
node.__setstate__('>=1.0') Defensive patterns
Strategy: type-guard
Validate before calling
def restore_node_value(node, value):
if not isinstance(value, str):
raise TypeError(f'value must be str, got {type(value).__name__}')
node._restore_value(value) Type guard
def is_str_state(value: object) -> bool:
return isinstance(value, str) Try / catch
try:
node.__setstate__(payload)
except TypeError as e:
if 'Cannot restore' in str(e):
raise ValueError(f'invalid pickle payload for node: {payload!r}') from e
raise Prevention
- Do not pickle packaging AST nodes; store the source string and re-parse
- Pin packaging versions across environments that exchange pickles
- Avoid monkeypatching __reduce__/__getstate__ on Node subclasses
When it happens
Trigger: Calling `__setstate__` directly with a non-string, unpickling a marker node from a pickle produced by tampering or by a different class hierarchy, or deepcopy when `__reduce__` was monkeypatched to return a non-string.
Common situations: Loading a pickle (e.g. cached parsed marker/specifier) from an older packaging version with a monkeypatch; serialization frameworks (dill, cloudpickle) that reconstruct state from non-str; test fixtures that hand-craft `__setstate__` payloads.
Related errors
- Cannot restore {self.__class__.__name__} from {state!r}
- Cannot restore Requirement from {state!r}
- Cannot restore Specifier from {state!r}
- Cannot restore SpecifierSet from {state!r}
- Cannot restore Tag from {state!r}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/19e72ef59a9611a6.json.
Report an issue: GitHub.