pypa/pip · error · TypeError
Cannot restore value from
Error message
Cannot restore {self.__class__.__name__} value from {value!r} What it means
Node is the base class for AST nodes in packaging's dependency-specifier parser (Variable, Value, Op). The _restore_value method is called during unpickling (__setstate__). If the value being restored is not a str, it raises TypeError. This guards against reconstructing parser nodes from invalid data. __setstate__ supports three pickle formats: new compact string (26.2+), old __slots__ tuple (<=26.0), and old __dict__ (<=25.0).
Solutions
- Store serialized strings (via node.serialize()) and re-parse on load instead of pickling Node objects directly
- Clear pickle caches when upgrading packaging versions
- Catch TypeError on unpickle and fall back to re-parsing from the original requirement string
Example fix
# before import pickle node = pickle.loads(cached_data) # may raise TypeError # after # Store the original requirement/marker string, not pickled Node objects from packaging.requirements import Requirement cached_str = "django>=4.0; python_version >= '3.8'" # On load, always re-parse from string (version-independent): req = Requirement(cached_str)
Defensive patterns
Strategy: try-catch
Validate before calling
def safe_unpickle_node(data, original_source=None):
import pickle
try:
return pickle.loads(data)
except TypeError:
return None # signal re-parse needed Type guard
def is_valid_node_value(value) -> bool:
return isinstance(value, str) Try / catch
try:
node = pickle.loads(cached_data)
except TypeError as e:
if "Cannot restore" in str(e):
from packaging._parser import parse_marker
node = parse_marker(original_marker_string) Prevention
- Do not pickle internal parser nodes; store serialized strings instead
- Re-parse requirement strings on load rather than unpickling
- Clear caches when upgrading packaging versions
When it happens
Trigger: Unpickling a Variable, Value, or Op node where the stored value is not a string: a corrupted pickle, a hand-crafted pickle, or an incompatible serialization from a fork or modified version of packaging.
Common situations: Loading cached requirement/marker parse results from disk across packaging version upgrades where the pickle format changed. Deserializing pickled marker data from an untrusted or third-party source.
Related errors
- Cannot restore from
- Algorithm used in hash field has different value in hashes…
- Algorithm used in hash field is not present in hashes field
- Cannot restore Requirement from
- Cannot restore Specifier from
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/19e72ef59a9611a6.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/_parser.py:37
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 f399c37189)