{"record":{"id":"28dd0bc6e78e892e","repo":"pydantic/monty","slug":"heap-boundary-uuid-entry-already-freed","errorCode":null,"errorMessage":"Heap::boundary_uuid: entry already freed","messagePattern":"Heap::boundary_uuid: entry already freed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/monty/src/heap/mod.rs","lineNumber":1295,"sourceCode":"            }\n            Some(WeakIndexKey::HostType(uuid)) if self.host_type_index.get(&uuid) == Some(&id) => {\n                self.host_type_index.remove(&uuid);\n            }\n            Some(WeakIndexKey::Boundary(_) | WeakIndexKey::HostType(_)) | None => {}\n        }\n    }\n\n    /// Boundary uuid of the sandbox class or instance at `id`, generated and\n    /// indexed on its first crossing to the host so the host can hand the\n    /// object back by id (see [`Heap::resolve_boundary_uuid`]).\n    ///\n    /// # Panics\n    /// If `id` is not a live `Instance` or `Class` entry.\n    pub(crate) fn boundary_uuid(&mut self, id: HeapId) -> MontyUuid {\n        let mut entry = self\n            .entries\n            .entry(id)\n            .expect(\"Heap::boundary_uuid: entry already freed\");\n        let uuid = match entry.get_mut().data.0.get_mut() {\n            HeapData::Instance(instance) => instance.boundary_uuid(),\n            HeapData::Class(class) => class.boundary_uuid(),\n            _ => unreachable!(\"Heap::boundary_uuid: only classes and instances carry a boundary uuid\"),\n        };\n        self.boundary_index.insert(uuid, id);\n        uuid\n    }\n\n    /// The live sandbox class or instance that crossed to the host as `uuid`,\n    /// if it still exists; the index holds no reference, so the returned id is\n    /// borrowed.\n    #[must_use]\n    pub(crate) fn resolve_boundary_uuid(&self, uuid: &MontyUuid) -> Option<HeapId> {\n        self.boundary_index.get(uuid).copied()\n    }\n\n    /// The live type object for the host class `uuid`, if the sandbox holds","sourceCodeStart":1277,"sourceCodeEnd":1313,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty/src/heap/mod.rs#L1277-L1313","documentation":"`Heap::boundary_uuid` panics if `entries.entry(id)` returns `None` (the entry was already freed) or, one line later, if the entry is not an `Instance` or `Class`. It is used to obtain the boundary uuid identifying a class/instance for the host boundary (snapshot/pool identity). A panic means a caller passed a stale id or a non-boundary type — an internal invariant violation.","triggerScenarios":"Calling `boundary_uuid` with a `HeapId` of an object already freed by refcount drop or cycle collection; passing the id of a List/Dict/String instead of an Instance/Class.","commonSituations":"Snapshotting or resolving boundary identity after the referenced object was dropped; type confusion where a value's type was assumed to be class/instance without checking.","solutions":["Check liveness before calling: use `try_entry`-style lookup or verify the ref is still reachable.","Verify the value's type is Instance/Class before requesting a boundary uuid.","Fix the upstream drop ordering that frees the object before boundary resolution completes (audit `drop_with`/GC paths)."],"exampleFix":"// before\nlet uuid = vm.heap.boundary_uuid(id); // id may be freed or non-class\n// after\nif let Some(kind) = reader.heap.try_entry_kind(id) {\n    assert!(matches!(kind, Instance | Class));\n    let uuid = vm.heap.boundary_uuid(id);\n}","handlingStrategy":"type-guard","validationCode":"// confirm type before requesting a boundary uuid\nassert!(matches!(reader.heap.entries.entry(id).map(|e| e.data_kind()), Some(Instance | Class)), 'id must be a live Instance or Class');","typeGuard":"fn is_boundary_object(vm: &VM, value: &Value) -> bool { matches!(value, Value::Ref(_)) && matches!(value.py_type(vm), Type::Instance(_) | Type::Class(_)) }","tryCatchPattern":null,"preventionTips":["Resolve boundary uuids before the object can be dropped (call early in snapshot flows).","Check the value type is Instance/Class before calling.","Audit drop/GC ordering for objects whose ids are handed to host-boundary APIs."],"tags":["rust","heap","panic","snapshot","boundary"],"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-14T11:17:12.474Z"}