GitoxideLabs/gitoxide · error

ids should be ordered, and we make sure to keep ahead with…

Error message

ids should be ordered, and we make sure to keep ahead with them

What it means

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.

Solutions

  1. Sort entries by full object ID (ascending, lexicographic big-endian byte order) before passing to write_to
  2. Deduplicate or otherwise ensure IDs strictly advance so the iterator never moves backwards
  3. Add a pre-write assertion that sorts and validates the id sequence in your packing code

Example fix

// before
index::write_to(entries_in_insertion_order, ...)
// after
entries.sort_by(|a, b| a.id.as_bytes().cmp(b.id.as_bytes()));
index::write_to(entries, ...)
Defensive patterns

Strategy: validation

Validate before calling

fn assert_sorted(ids: &[gix_hash::ObjectId]) -> bool {
    ids.windows(2).all(|w| w[0] <= w[1])
}
assert!(assert_sorted(&ids), "ids must be ascending before index::write_to");

Try / catch

// unreachable! panics; guard by validating before the call
if !assert_sorted(&ids) { return Err("ids must be sorted by object id"); }
let bytes = index::write_to(entries.iter(), ...)?;

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/eeb42b75bc06a7e4. Report an issue: GitHub.

Appendix: source

Thrown at gix-pack/src/index/encode.rs:16

use std::cmp::Ordering;

pub(crate) const LARGE_OFFSET_THRESHOLD: u64 = 0x7fff_ffff;
pub(crate) const HIGH_BIT: u32 = 0x8000_0000;

pub(crate) fn fanout(iter: &mut dyn ExactSizeIterator<Item = u8>) -> [u32; 256] {
    let mut fan_out = [0u32; 256];
    let entries_len = iter.len() as u32;
    let mut iter = iter.enumerate();
    let mut idx_and_entry = iter.next();
    let mut upper_bound = 0;

    for (offset_be, byte) in fan_out.iter_mut().zip(0u8..=255) {
        *offset_be = match idx_and_entry.as_ref() {
            Some((_idx, first_byte)) => match first_byte.cmp(&byte) {
                Ordering::Less => unreachable!("ids should be ordered, and we make sure to keep ahead with them"),
                Ordering::Greater => upper_bound,
                Ordering::Equal => {
                    if byte == 255 {
                        entries_len
                    } else {
                        idx_and_entry = iter.find(|(_, first_byte)| *first_byte != byte);
                        upper_bound = idx_and_entry.as_ref().map_or(entries_len, |(idx, _)| *idx as u32);
                        upper_bound
                    }
                }
            },
            None => entries_len,
        };
    }

    fan_out
}

View on GitHub (pinned to e73179060b)