GitoxideLabs/gitoxide · error
BUG: instance must be initialized for each search set
Error message
BUG: instance must be initialized for each search set
What it means
`Outcome::remaining()` returns the count of attributes not yet found, stored in an `Option<usize>` that is only set once `initialize()`/selection setup runs for a search set. The `.expect("BUG: instance must be initialized for each search set")` panics when `remaining()` is called on an `Outcome` that was created but never initialized for the current attribute search set.
Solutions
- Ensure the Outcome is produced by (or initialized through) the attribute search pipeline before reading `remaining()`
- Re-create the Outcome for each search set instead of reusing an instance across searches
- Upgrade gix — newer versions may make initialization implicit
- If you control the code path, call the initializer before any `remaining()`/iteration use
Example fix
// before let outcome = Outcome::default(); let left = outcome.remaining(); // after: derive Outcome from the search let outcome = collection.initialize(selected_attributes); // or obtain from search let left = outcome.remaining();
Defensive patterns
Strategy: type-guard
Validate before calling
// ensure the Outcome came from the search pipeline
fn outcome_ready(o: &gix_attributes::search::Outcome) -> bool {
format!("{:?}", o).contains("remaining: Some") // Debug shows the Option
} Type guard
fn is_initialized(outcome: &Outcome) -> bool {
// initialize-then-use: only read remaining() right after a search produced it
std::ptr::eq(outcome as *const _, outcome as *const _) && !outcome_is_fresh(outcome)
} Try / catch
let left = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| outcome.remaining()));
Prevention
- Never construct Outcome manually; obtain it from collection/search APIs
- Re-create the Outcome for each search set
- Do not cache Outcomes across attribute queries
When it happens
Trigger: Calling `Outcome::remaining()` (via the public search APIs that expose it) on an Outcome obtained without running a search/selection initialization pass — e.g. constructing an Outcome manually or reusing a stale Outcome across different search sets.
Common situations: Reusing a cached `Outcome` struct across multiple `gix-attributes` searches without re-initializing; custom code that mirrors gix's internal Outcome lifecycle when integrating the crate.
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
- initialized
- just added
- valid macro names are always UTF8 and this was verified
- ' ' is not a valid configuration key
- Cannot use iter_v1() on index of type
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/c6a3af368137566f.
Report an issue: GitHub.
Appendix: source
Thrown at gix-attributes/src/search/outcome.rs:227
.map(|attr| (attr.id, attr.inner.clone(), Some(id))),
);
}
}
false
}
}
impl Outcome {
/// Given a list of `attrs` by order, return true if at least one of them is not set
pub(crate) fn has_unspecified_attributes(&self, mut attrs: impl Iterator<Item = AttributeId>) -> bool {
attrs.any(|order| self.matches_by_id[order.0].r#match.is_none())
}
/// Return the amount of attributes haven't yet been found.
///
/// If this number reaches 0, then the search can be stopped as there is nothing more to fill in.
pub(crate) fn remaining(&self) -> usize {
self.remaining
.expect("BUG: instance must be initialized for each search set")
}
fn reduce_and_check_if_done(&mut self, attr: AttributeId) -> bool {
if self.selected.is_empty() || self.selected.iter().any(|(_name, id)| *id == Some(attr)) {
*self.remaining.as_mut().expect("initialized") -= 1;
}
self.is_done()
}
}
impl std::fmt::Debug for Outcome {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
struct AsDisplay<'a>(&'a dyn std::fmt::Display);
impl std::fmt::Debug for AsDisplay<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}View on GitHub (pinned to e73179060b)