pydantic/monty · error

dict_keys view must always reference a dict

Error message

dict_keys view must always reference a dict

What it means

The HeapRead-based DictKeysView accessor reads the backing dictionary from the heap and panics if the id does not resolve to a Dict entry. As with the trait version, this is unreachable by construction and signals heap corruption or an id-reuse bug under the safe HeapReader API.

Source

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

impl DictKeysView {
    /// Creates a new keys view over an existing dictionary heap entry.
    #[must_use]
    pub fn new(dict_id: HeapId) -> Self {
        Self { dict_id }
    }

    /// Returns the underlying dictionary heap id.
    #[must_use]
    pub fn dict_id(self) -> HeapId {
        self.dict_id
    }
}

impl<'h> HeapRead<'h, DictKeysView> {
    fn dict(&self, vm: &mut VM<'h>) -> HeapObjectRead<'h, Dict> {
        let HeapReadOutput::Dict(dict) = vm.heap.read(self.get(vm.heap).dict_id) else {
            panic!("dict_keys view must always reference a dict");
        };
        dict
    }

    /// Compares this keys view to a mutable set using set membership semantics.
    pub(crate) fn eq_set(&self, other: &HeapRead<'h, Set>, vm: &mut VM<'h>) -> RunResult<bool> {
        dict_keys_eq_set_like(
            &self.dict(vm),
            other.get(vm.heap).len(),
            |key, vm| other.contains(key, vm),
            vm,
        )
    }

    /// Compares this keys view to a frozenset using set membership semantics.
    pub(crate) fn eq_frozenset(&self, other: &HeapRead<'h, FrozenSet>, vm: &mut VM<'h>) -> RunResult<bool> {
        dict_keys_eq_set_like(
            &self.dict(vm),

View on GitHub (pinned to adc986b362)

Solutions

  1. Fix the refcount path that lets the dict be freed while the view exists (views must count as readers/refs).
  2. Verify the view's dict_id is incremented at construction and released via py_dec_ref_ids.
  3. Run the relevant test binary with `--features memory-model-checks` to catch the stale-id path.

Example fix

// before
let view = DictKeysView { dict_id }; // borrowed id
// after
heap.inc_ref(dict_id);
let view = DictKeysView { dict_id }; // owned, cleaned up via py_dec_ref_ids
Defensive patterns

Strategy: type-guard

Validate before calling

// check the read resolves to a Dict before further use
if !matches!(vm.heap.read(view.get(vm.heap).dict_id), HeapReadOutput::Dict(_)) { return Err(ViewError::stale_dict()); }

Prevention

When it happens

Trigger: Any keys-view operation (eq_set, iteration, len) when the view's stored dict_id resolves to a non-Dict HeapReadOutput — i.e. the dict was freed and its id recycled while the view handle was live.

Common situations: Memory-model CI after refcount changes; debugging reports of wrong-typed heap reads.

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/c6b0e96383135d33. Report an issue: GitHub.