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
- Fix the lifetime/refcount path that allowed the view's HeapId to be freed or reassigned while the view lived.
- Ensure views hold a counted reference to the dict (dec_ref participation via py_dec_ref_ids).
- 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
- Views must hold counted references to their backing dict
- Never copy dict_id out of a view without inc_ref
- Run memory-model-checks on changes to view or heap lifetime code
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
- dict_keys view must always reference a dict
- entry just allocated
- gather commit frame id is not a GatherFuture
- gather item is not a Coroutine, ExternalFuture, or GatherFut
- Cannot get identity of Dereferenced object
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/f0ba9eb4c5994132.
Report an issue: GitHub.