{"record":{"id":"93924d21be30d91f","repo":"pydantic/monty","slug":"dict-items-view-must-always-reference-a-dict","errorCode":null,"errorMessage":"dict_items view must always reference a dict","messagePattern":"dict_items view must always reference a dict","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/monty/src/types/dict_view.rs","lineNumber":294,"sourceCode":"\nimpl DictItemsView {\n    /// Creates a new items view over an existing dictionary heap entry.\n    #[must_use]\n    pub fn new(dict_id: HeapId) -> Self {\n        Self { dict_id }\n    }\n\n    /// Returns the underlying dictionary heap id.\n    #[must_use]\n    pub fn dict_id(self) -> HeapId {\n        self.dict_id\n    }\n}\n\nimpl<'h> HeapRead<'h, DictItemsView> {\n    fn dict(&self, vm: &mut VM<'h>) -> HeapObjectRead<'h, Dict> {\n        let HeapReadOutput::Dict(dict) = vm.heap.read(self.get(vm.heap).dict_id) else {\n            panic!(\"dict_items view must always reference a dict\");\n        };\n        dict\n    }\n\n    /// Compares this items view to a mutable set using set membership semantics.\n    pub(crate) fn eq_set(&self, other: &HeapRead<'h, Set>, vm: &mut VM<'h>) -> RunResult<bool> {\n        dict_items_eq_set_like(\n            &self.dict(vm),\n            other.get(vm.heap).len(),\n            |item, vm| other.contains(item, vm),\n            vm,\n        )\n    }\n\n    /// Compares this items view to a frozenset using set membership semantics.\n    pub(crate) fn eq_frozenset(&self, other: &HeapRead<'h, FrozenSet>, vm: &mut VM<'h>) -> RunResult<bool> {\n        dict_items_eq_set_like(\n            &self.dict(vm),","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty/src/types/dict_view.rs#L276-L312","documentation":"Internal panic in the `DictItemsView::dict()` helper: the view's stored `dict_id` must always resolve to a Dict heap entry. The helper is used by items-view operations (comparison, conversion); reaching the panic means the view points at a non-Dict heap object, breaking the view invariant.","triggerScenarios":"Any operation on a `dict.items()` view that calls the private `dict()` accessor (e.g. `eq_set` against a set, conversion to dict) while the view's dict_id resolves to a non-Dict entry — only via interpreter bugs or heap corruption.","commonSituations":"Encountered by Monty contributors during heap/refcount changes or when views outlive dictionary modifications in a buggy path; not reachable from valid Python code.","solutions":["File a bug with the reproducing snippet and full panic output.","Verify every DictItemsView construction site stores the correct, live dict HeapId.","Run memory-model-checks tests (`cargo test -p monty --features memory-model-checks`) on relevant test binaries to catch refcount errors."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match vm.heap.read(dict_id) {\n    HeapReadOutput::Dict(d) => d,\n    _ => return Err(internal_error(\"dict_items view lost its backing dict\")),\n}","preventionTips":["Construct views only from a live Dict HeapId obtained in the same scope.","Run ref-count-return and memory-model-checks tests after heap changes.","Review drop paths for views to ensure the dict entry cannot be freed first."],"tags":["rust","internal-error","panic","dict-view"],"backgroundTag":"internal-invariant-violation","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}