{"record":{"id":"eeb42b75bc06a7e4","repo":"GitoxideLabs/gitoxide","slug":"ids-should-be-ordered-and-we-make-sure-to-keep-ah","errorCode":null,"errorMessage":"ids should be ordered, and we make sure to keep ahead with them","messagePattern":"ids should be ordered, and we make sure to keep ahead with them","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-pack/src/index/encode.rs","lineNumber":16,"sourceCode":"use std::cmp::Ordering;\n\npub(crate) const LARGE_OFFSET_THRESHOLD: u64 = 0x7fff_ffff;\npub(crate) const HIGH_BIT: u32 = 0x8000_0000;\n\npub(crate) fn fanout(iter: &mut dyn ExactSizeIterator<Item = u8>) -> [u32; 256] {\n    let mut fan_out = [0u32; 256];\n    let entries_len = iter.len() as u32;\n    let mut iter = iter.enumerate();\n    let mut idx_and_entry = iter.next();\n    let mut upper_bound = 0;\n\n    for (offset_be, byte) in fan_out.iter_mut().zip(0u8..=255) {\n        *offset_be = match idx_and_entry.as_ref() {\n            Some((_idx, first_byte)) => match first_byte.cmp(&byte) {\n                Ordering::Less => unreachable!(\"ids should be ordered, and we make sure to keep ahead with them\"),\n                Ordering::Greater => upper_bound,\n                Ordering::Equal => {\n                    if byte == 255 {\n                        entries_len\n                    } else {\n                        idx_and_entry = iter.find(|(_, first_byte)| *first_byte != byte);\n                        upper_bound = idx_and_entry.as_ref().map_or(entries_len, |(idx, _)| *idx as u32);\n                        upper_bound\n                    }\n                }\n            },\n            None => entries_len,\n        };\n    }\n\n    fan_out\n}\n","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-pack/src/index/encode.rs#L1-L34","documentation":"While building the 256-slot fanout table of a pack index, entries are assumed to be sorted by their object ID. If the current entry's first ID byte is less than the fanout slot byte being filled, the iterator has gone backwards, meaning input IDs were not sorted; the code panics via unreachable! instead of returning an error.","triggerScenarios":"Calling `write_to` (via `gix_pack::index::write_to` / `Index::write_to`) with an iterator of `(entry, id)` pairs whose object IDs are not strictly ascending — e.g. entries collected in insertion order or sorted by a different key.","commonSituations":"Custom pack generation code feeding unsorted or duplicate-handled id lists; refactoring that changes sort order; sorting by OID as bytes on a platform with different byte-order assumptions.","solutions":["Sort entries by full object ID (ascending, lexicographic big-endian byte order) before passing to write_to","Deduplicate or otherwise ensure IDs strictly advance so the iterator never moves backwards","Add a pre-write assertion that sorts and validates the id sequence in your packing code"],"exampleFix":"// before\nindex::write_to(entries_in_insertion_order, ...)\n// after\nentries.sort_by(|a, b| a.id.as_bytes().cmp(b.id.as_bytes()));\nindex::write_to(entries, ...)","handlingStrategy":"validation","validationCode":"fn assert_sorted(ids: &[gix_hash::ObjectId]) -> bool {\n    ids.windows(2).all(|w| w[0] <= w[1])\n}\nassert!(assert_sorted(&ids), \"ids must be ascending before index::write_to\");","typeGuard":null,"tryCatchPattern":"// unreachable! panics; guard by validating before the call\nif !assert_sorted(&ids) { return Err(\"ids must be sorted by object id\"); }\nlet bytes = index::write_to(entries.iter(), ...)?;","preventionTips":["Always sort entries by full big-endian object id before index writing","Never sort by any secondary key without id as the primary key","Add a debug assertion for id ordering in pack-generation pipelines"],"tags":["rust","pack-index","sorting","panic"],"backgroundTag":"invalid-argument-value","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}