pydantic/monty · error
deserialize
Error message
deserialize
What it means
Panic from `postcard::from_bytes::<Heap>(&bytes).expect("deserialize")` (crates/monty/src/heap/mod.rs:2540), the second half of the same round-trip test. Deserializing failed: either the bytes were produced by an incompatible schema version (heap layout changed between serialize and deserialize paths) or `Heap`'s `Deserialize` impl rejects the encoded data (e.g. variant out of range, invalid enum discriminant, postcard format error).
Source
Thrown at crates/monty/src/heap/mod.rs:2540
#[test]
fn pending_purple_cycle_round_trips_through_serde() {
// A snapshot can be taken between any two bytecode instructions, so
// entries flagged Purple by `dec_ref` but not yet visited by the
// collector must survive serde round-trips. Otherwise a cycle that
// becomes garbage just before snapshot would leak permanently after
// restore (the post-restore VM would never re-touch it).
let mut heap = Heap::new(16, ResourceTracker::default());
let id = alloc_self_cycle(&heap);
// Drop the caller's external ref so the entry is genuinely
// unreachable except via its self-pointer. dec_ref flags Purple.
heap.dec_ref(id); // rc 2 → 1
assert_eq!(heap.purple_count, 1);
assert_eq!(heap.entries.get(id).color.get(), CcColor::Purple);
// Round-trip through postcard.
let bytes = postcard::to_allocvec(&heap).expect("serialize");
let mut restored: Heap = postcard::from_bytes(&bytes).expect("deserialize");
// `purple_count` and the per-entry color must round-trip.
assert_eq!(restored.purple_count, 1);
assert_eq!(restored.entries.get(id).color.get(), CcColor::Purple);
// Run the collector on the restored heap; the cycle is unreachable
// and must be reclaimed.
restored.collect_cycles();
assert!(!is_alive(&restored, id));
assert_eq!(restored.purple_count, 0);
}
#[test]
fn isolated_cycle_with_duplicate_child_refs_is_collected() {
// Regression: an unreachable cycle where one element references its
// sibling multiple times within its child list. The mark phase must
// (a) decrement the sibling's refcount once per edge, and (b) push
// the sibling onto the work stack at most once, even when manyView on GitHub (pinned to adc986b362)
Solutions
- Print or unwrap_err the postcard::Error to identify the failing field/variant.
- Regenerate both Serialize and Deserialize impls together so field order and variants match exactly.
- Ensure enum/struct derives use the same representation on both sides (no `#[serde(skip)]` on fields the Deserialize path requires).
- If the heap holds non-serializable state, serialize only the durable data and reconstruct transient state (colors, purple_count) in the Deserialize impl or after `from_bytes`.
Example fix
// before
let mut restored: Heap = postcard::from_bytes(&bytes).expect("deserialize");
// after
let mut restored: Heap = postcard::from_bytes(&bytes)
.unwrap_or_else(|e| panic!("deserialize failed: {e:?}")); Defensive patterns
Strategy: try-catch
Validate before calling
let bytes = postcard::to_allocvec(&heap).expect("serialize");
assert!(!bytes.is_empty(), "serialized heap must not be empty"); Try / catch
match postcard::from_bytes::<Heap>(&bytes) {
Ok(restored) => { /* proceed */ }
Err(e) => panic!("deserialize failed: {e:?}"),
} Prevention
- Update Serialize and Deserialize impls in lockstep — never one without the other.
- Avoid reordering or removing HeapData variants; add new variants only at the end.
- Surface the postcard::Error with unwrap_or_else instead of a bare expect so failures are diagnosable.
When it happens
Trigger: Deserialize mismatch after adding/reordering `HeapData` or `HeapEntry` fields so the wire layout no longer matches; a hand-written `Deserialize` impl that validates fields and errors on restored state (e.g. refcount or free-list invariants); or corrupted/empty byte input passed to `from_bytes`.
Common situations: Mid-refactor states where serialize uses new field layout but the Deserialize impl (or vice versa) is stale; tests that serialize a Heap with entries whose Deserialize path expects an initialized free list or page structure that postcard cannot reconstruct.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- serialize
- dict view must always reference a dict
- dict_keys view must always reference a dict
- HeapReader::read_ptr - id out of bounds
- HeapPtr::entry: slot has been freed
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/41ce450f900abd0f.
Report an issue: GitHub.