pydantic/monty · error

dict view must always reference a dict

Error message

dict view must always reference a dict

What it means

A dict view (keys/items/values) stores the HeapId of the dictionary it was created from and must always find a Dict at that id; any other HeapData variant is impossible by construction, so the helper panics. Hitting it means heap corruption or a HeapId that outlived its entry and was reused for another object.

Source

Thrown at crates/monty/src/types/dict_view.rs:33

        iter::checked_preallocation_hint,
    },
    value::{EitherStr, Value},
};

/// Shared accessors for heap-backed dictionary view objects.
///
/// All dictionary views are thin live references to an underlying `dict`. They do
/// not snapshot keys, items, or values; instead every observable operation reads
/// through to the current dict state. Keeping that behavior centralized avoids
/// subtle divergence between keys/items/values views.
pub(crate) trait DictView {
    /// Returns the heap id of the underlying dictionary this view keeps alive.
    fn dict_id(&self) -> HeapId;

    /// Returns the live dictionary backing this view.
    fn dict<'a>(&self, heap: &'a Heap) -> &'a Dict {
        let HeapData::Dict(dict) = heap.get(self.dict_id()) else {
            panic!("dict view must always reference a dict");
        };
        dict
    }
}

/// Live view returned by `dict.keys()`.
///
/// `dict_keys` is set-like in CPython, so this view supports the shared live-view
/// behavior plus equality against other keys views and ordinary set-like values.
/// The remaining set algebra operations are added incrementally in the VM layer.
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub(crate) struct DictKeysView {
    dict_id: HeapId,
}

impl DictKeysView {
    /// Creates a new keys view over an existing dictionary heap entry.
    #[must_use]

View on GitHub (pinned to adc986b362)

Solutions

  1. Fix the lifetime/refcount path that allowed the view's HeapId to be freed or reassigned while the view lived.
  2. Ensure views hold a counted reference to the dict (dec_ref participation via py_dec_ref_ids).
  3. Audit any code that copies a view's dict_id without incrementing the refcount.

Example fix

// before
let dict_id = view.dict_id(); // borrowed without refcount
// after
let dict_id = view.dict_id();
heap.inc_ref(dict_id); // keep the entry alive while borrowed
Defensive patterns

Strategy: type-guard

Validate before calling

// verify the entry is still a Dict before using the view
if !matches!(heap.get(view.dict_id()), HeapData::Dict(_)) { return Err(ViewError::stale_dict()); }

Prevention

When it happens

Trigger: Calling `dict(heap)` on a DictView trait object when the backing HeapId now points at a non-Dict entry — only reachable if refcount cleanup freed the dict and the id was recycled while the view stayed alive.

Common situations: Memory-model-checks runs after changes to heap cleanup or view lifetime tracking; contributors investigating heap corruption reports.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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