GitoxideLabs/gitoxide · error
counts were resolved beforehand
Error message
counts were resolved beforehand
What it means
In `iter_from_counts` (gix-pack `data/output/entry/iter_from_counts.rs`), the sorting comparator assumes every `Count` has been resolved to a `LookedUp` pack location, so comparing anything else is declared unreachable. The `unreachable!()` fires when a count in `NotLookedUp` state participates in the sort, meaning the caller passed counts whose pack locations were never resolved.
Solutions
- Ensure all counts have resolved pack locations (run the count/lookup iteration) before passing them to `iter_from_counts`.
- Filter or reject `NotLookedUp` counts before calling (see validation code), or re-resolve them against the ODB.
- If intentionally packing without locations, use the code path that doesn't require resolved counts.
Example fix
// before
let iter = iter_from_counts(counts, ...)?; // panics if some counts are NotLookedUp
// after
if counts.iter().any(|c| matches!(c.entry_pack_location, PackLocation::NotLookedUp)) {
return Err(anyhow!("all counts must have resolved pack locations"));
}
let iter = iter_from_counts(counts, ...)?; Defensive patterns
Strategy: validation
Validate before calling
// Reject unresolved counts before constructing the iterator
if counts.iter().any(|c| matches!(c.entry_pack_location, PackLocation::NotLookedUp)) {
return Err(anyhow!("counts must be location-resolved before iter_from_counts"));
} Type guard
fn all_resolved(counts: &[Count]) -> bool {
counts.iter().all(|c| matches!(c.entry_pack_location, PackLocation::LookedUp(_)))
} Try / catch
// Wrap iterator construction to convert panic into an error
let iter = std::panic::catch_unwind(AssertUnwindSafe(|| iter_from_counts(counts, ...)))
.map_err(|p| anyhow!("unresolved counts passed to iter_from_counts: {:?}", p))??; Prevention
- Only feed counts into `iter_from_counts` that came from a full lookup/resolution pass.
- Add an assertion/filter for `NotLookedUp` counts at API boundaries.
- When mixing counts from multiple sources, re-resolve them all against the same ODB first.
When it happens
Trigger: Calling the public `iter_from_counts` constructor with counts whose `entry_pack_location` values include `PackLocation::NotLookedUp` (locations not resolved beforehand); the `_ => _` comparator arm then panics.
Common situations: Building pack output from counts collected without the lookup/resolution phase, mixing counts from different configurations (some resolved, some not), or version changes in how counts are produced upstream.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- must have been resolved
- Cannot use iter_v1() on index of type
- Cannot use iter_v2() on index of type
- only value and unspecified are possible here
- parent-match assures this
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/01e0f49c48909a1a.
Report an issue: GitHub.
Appendix: source
Thrown at gix-pack/src/data/output/entry/iter_from_counts.rs:112
.expect("infallible - we ignore none-existing objects");
progress.lock().show_throughput(start);
}
let counts_range_by_pack_id = match mode {
Mode::PackCopyAndBaseObjects => {
let mut progress = progress.add_child_with_id("sorting".into(), ProgressId::SortEntries.into());
progress.init(Some(counts.len()), gix_features::progress::count("counts"));
let start = std::time::Instant::now();
use crate::data::output::count::PackLocation::*;
counts.sort_by(|lhs, rhs| match (&lhs.entry_pack_location, &rhs.entry_pack_location) {
(LookedUp(None), LookedUp(None)) => Ordering::Equal,
(LookedUp(Some(_)), LookedUp(None)) => Ordering::Greater,
(LookedUp(None), LookedUp(Some(_))) => Ordering::Less,
(LookedUp(Some(lhs)), LookedUp(Some(rhs))) => lhs
.pack_id
.cmp(&rhs.pack_id)
.then(lhs.pack_offset.cmp(&rhs.pack_offset)),
(_, _) => unreachable!("counts were resolved beforehand"),
});
let mut index: Vec<(u32, std::ops::Range<usize>)> = Vec::new();
let mut chunks_pack_start = counts.partition_point(|e| e.entry_pack_location.is_none());
let mut slice = &counts[chunks_pack_start..];
while !slice.is_empty() {
let current_pack_id = slice[0].entry_pack_location.as_ref().expect("packed object").pack_id;
let pack_end = slice.partition_point(|e| {
e.entry_pack_location.as_ref().expect("packed object").pack_id == current_pack_id
});
index.push((current_pack_id, chunks_pack_start..chunks_pack_start + pack_end));
slice = &slice[pack_end..];
chunks_pack_start += pack_end;
}
progress.set(counts.len());
progress.show_throughput(start);
View on GitHub (pinned to e73179060b)