{"record":{"id":"aef157878beb4e7c","repo":"BoundaryML/baml","slug":"live-owner-must-be-forwarded","errorCode":null,"errorMessage":"live owner must be forwarded","messagePattern":"live owner must be forwarded","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"baml_language/crates/bex_vm/src/package_load.rs","lineNumber":624,"sourceCode":"        let (mut tlab, owners) = register_witnesses(&heap, &tables, interface, 128);\n\n        // Root every other owner AND its rule, as a live class's own reachability\n        // would (an instance reaches the class; the class's provenance/type value\n        // reaches its witnesses).\n        let mut live: Vec<_> = owners.iter().step_by(2).copied().collect();\n        live.extend(tables.rules_of(interface).into_iter().step_by(2));\n        #[expect(unsafe_code, reason = \"standalone stop-the-world GC test\")]\n        let (_stats, _remapped, forwarding) = unsafe { heap.collect_garbage(&live) };\n        hook.forward_roots(&forwarding);\n        assert_eq!(\n            tables.rule_count(),\n            64,\n            \"dead owners must sweep their rules\"\n        );\n        let forwarded_owner = forwarding\n            .get(&owners[0])\n            .copied()\n            .expect(\"live owner must be forwarded\");\n        assert!(\n            !tables.rules_for_class(forwarded_owner).is_empty(),\n            \"live owner pointers must follow relocation\"\n        );\n        for rule in tables.rules_of(interface) {\n            #[expect(unsafe_code, reason = \"reading a just-forwarded pointer\")]\n            let obj = unsafe { rule.get() };\n            assert!(\n                matches!(obj, Object::ImplRule(_)),\n                \"rule pointers must follow relocation\"\n            );\n        }\n\n        tlab.invalidate();\n        #[expect(unsafe_code, reason = \"standalone stop-the-world GC test\")]\n        let (_stats, _remapped, forwarding) = unsafe { heap.collect_garbage(&[]) };\n        hook.forward_roots(&forwarding);\n        assert_eq!(","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bex_vm/src/package_load.rs#L606-L642","documentation":"This is a test assertion in `dynamic_dispatch_entries_are_forwarded_then_swept_with_owners` that fails when a GC forwarding map does not contain an entry for an owner that the test marked live. After sweeping, every surviving object must appear in the forwarding table with its new address; a missing entry means the sweep either collected a live object or failed to record its relocation, which would corrupt dynamic dispatch for that class.","triggerScenarios":"Running the VM GC tests where `owners[0]` is expected to survive the sweep but `forwarding.get(&owners[0])` returns None — e.g. the object wasn't rooted/marked before sweep_and_forward, or the sweep skipped recording a forwarding entry for a live object.","commonSituations":"Changes to the GC marking or forwarding logic; tests that forget to keep a reference alive across the sweep; regressions where sweep_and_forward drops entries whose owners are still reachable.","solutions":["Verify the owner is actually rooted/marked live before sweeping; if it's intentionally dead, update the test to expect no forwarding entry.","Check that sweep_and_forward records a forwarding entry for every object that survives marking.","Run the GC tests with debug assertions and a smaller heap to catch which phase loses the object.","Inspect recent changes to marking/roots and revert or fix the regression."],"exampleFix":"// before (test assumes liveness not established)\nsweep_and_forward();\nlet forwarded_owner = forwarding.get(&owners[0]).copied().expect(\"live owner must be forwarded\");\n// after (keep the owner alive across the sweep)\nlet keep_alive: Vec<HeapPtr> = owners.clone(); // reachable during sweep\nsweep_and_forward();\nlet forwarded_owner = forwarding.get(&keep_alive[0]).copied().expect(\"live owner must be forwarded\");","handlingStrategy":"validation","validationCode":"// test-side check before asserting\nassert!(owners.iter().all(|o| is_marked_live(o)), \"all owners must be rooted before sweep_and_forward\");","typeGuard":"// narrow the forwarding result before use\nlet forwarded_owner: Option<HeapPtr> = forwarding.get(&owners[0]).copied();\nif let Some(owner) = forwarded_owner { /* proceed */ }","tryCatchPattern":null,"preventionTips":["Keep references to objects that must survive a sweep so the marker sees them as live roots.","After GC changes, run the dispatch-forwarding GC tests before merging.","Verify sweep_and_forward records a forwarding entry for every surviving object."],"tags":["rust","gc","test-failure","vm"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}