{"record":{"id":"0cbe10c8c1f7aaa6","repo":"GitoxideLabs/gitoxide","slug":"bug-a-slot-can-never-be-deleted-if-we-have-it-rec","errorCode":null,"errorMessage":"BUG: a slot can never be deleted if we have it recorded in the index WHILE changing said index. There shouldn't be a race","messagePattern":"BUG: a slot can never be deleted if we have it recorded in the index WHILE changing said index\\. There shouldn't be a race","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-odb/src/store_impls/dynamic/load_index.rs","lineNumber":655,"sourceCode":"                        .expect(\"BUG: cannot change from something to nothing, would be race\");\n                    files_mut.put_back();\n                    debug_assert_eq!(\n                        files_mut.mtime(),\n                        mtime,\n                        \"BUG: we can only put back files that didn't obviously change\"\n                    );\n                    // Safety: can't race as we hold the lock, must be set before replacing the data.\n                    // NOTE that we don't change the generation as it's still the very same index we talk about, it doesn't change\n                    // identity.\n                    slot.generation.store(current_generation, Ordering::SeqCst);\n                    slot.files.store(files);\n                } else {\n                    // it's already in the correct state, either loaded or unloaded.\n                }\n                bundle.index_is_loaded()\n            }\n            None => {\n                unreachable!(\n                    \"BUG: a slot can never be deleted if we have it recorded in the index WHILE changing said index. There shouldn't be a race\"\n                )\n            }\n        }\n    }\n\n    /// Stability means that indices returned by this API will remain valid.\n    /// Without that constraint, we may unload unused packs and indices, and may rebuild the slotmap index.\n    ///\n    /// Note that this must be called with a lock to the relevant state held to assure these values don't change while\n    /// we are working on said index.\n    fn maintain_stable_indices(&self, _guard: &parking_lot::MutexGuard<'_, ()>) -> bool {\n        self.num_handles_stable.load(Ordering::SeqCst) > 0\n    }\n\n    pub(crate) fn collect_snapshot(&self) -> Snapshot {\n        // We don't observe changes-on-disk in our 'wait-for-load' loop.\n        // That loop is meant to help assure the marker (which includes the amount of loaded indices) matches","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-odb/src/store_impls/dynamic/load_index.rs#L637-L673","documentation":"`assure_slot_matches_index` in `gix-odb`'s dynamic object store looks up a pack slot recorded in an in-memory index; the `None` arm panics because a slot being consulted cannot be removed concurrently by design (single-writer access to the index). Hitting it means the slot bookkeeping was mutated unexpectedly.","triggerScenarios":"Only if slot replacement/removal logic in the multi-pack index races or double-frees an entry — e.g. a refactor bug around `MultiIndexSlotMap` removal while a `load_index` operation holds a lookup.","commonSituations":"Not expected in normal use of a repository with many pack files and multi-pack indices; would appear as a panic while loading objects.","solutions":["Report upstream with repository layout (pack files, multi-pack-index) and the operation that panicked.","Update `gix-odb`/`gix` to the latest version.","If maintaining, replace `unreachable!` with an error indicating a lost slot for diagnosability."],"exampleFix":"// before\nNone => unreachable!(\"BUG: a slot can never be deleted ...\"),\n// after\nNone => return Err(message(\"BUG: multi-index slot missing while consulting index; store state corrupted\"))","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Not user-triggerable; wrap object loads in catch_unwind for resilience.\nlet obj = std::panic::catch_unwind(|| repo.find_object(id));","preventionTips":["Avoid custom slot/index manipulation in forked code","Keep gix-odb current","Verify repository integrity with fsck-style checks"],"tags":["rust","panic","internal-invariant","object-database"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}