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 many

View on GitHub (pinned to adc986b362)

Solutions

  1. Print or unwrap_err the postcard::Error to identify the failing field/variant.
  2. Regenerate both Serialize and Deserialize impls together so field order and variants match exactly.
  3. Ensure enum/struct derives use the same representation on both sides (no `#[serde(skip)]` on fields the Deserialize path requires).
  4. 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

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


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/41ce450f900abd0f. Report an issue: GitHub.