GitoxideLabs/gitoxide · error

next

Error message

next

What it means

Panic raised by `refs_sorted.next().expect("next")` in the packed-refs transaction commit loop. `peek()` had just returned `Some(Err(_))`, so `next()` must also return `Some`; if not, the peekable iterator's state contradicts itself — an internal invariant. The immediate `expect_err` converts the item into the iteration error reported to the caller.

Solutions

  1. Treat any occurrence as a gitoxide bug and report it with the stack trace and packed-refs contents.
  2. If the underlying cause is an iteration error, repair the corrupt packed-refs file.
  3. Use a gix-ref version without the faulty peek/next interplay.
Defensive patterns

Strategy: validation

Validate before calling

// pre-validate packed-refs parses cleanly to avoid the iteration-error path
for r in buffer.iter() { r.map_err(|e| format!("corrupt packed-refs: {e}"))?; }

Try / catch

// wrap the transaction in catch_unwind at a boundary if input is untrusted
let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| tx.commit()));

Prevention

When it happens

Trigger: Reaching the `(Some(Err(_)), _)` match arm during `commit()` where the peeked iteration error disappears on `next()` — logically impossible with `std::iter::Peekable`; only a bug (e.g. misuse of the iterator between peek and next) could trigger it.

Common situations: Not user-reachable under correct library use; it surfaces only when a packed-refs iteration error occurs (corrupt file) AND an internal iterator invariant is already broken.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at gix-ref/src/store/packed/transaction.rs:181

        let mut file = self.lock.expect("a write lock for applying changes");
        let refs_sorted: Box<dyn Iterator<Item = Result<packed::Reference<'_>, packed::iter::Error>>> =
            match self.buffer.as_ref() {
                Some(buffer) => Box::new(buffer.iter()?),
                None => Box::new(std::iter::empty()),
            };

        let mut refs_sorted = refs_sorted.peekable();

        edits.sort_by(|l, r| l.inner.name.as_bstr().cmp(r.inner.name.as_bstr()));
        let mut peekable_sorted_edits = edits.iter().peekable();

        file.with_mut(|f| f.write_all(HEADER_LINE))?;

        let mut num_written_lines = 0;
        loop {
            match (refs_sorted.peek(), peekable_sorted_edits.peek()) {
                (Some(Err(_)), _) => {
                    let err = refs_sorted.next().expect("next").expect_err("err");
                    return Err(commit::Error::Iteration(err));
                }
                (None, None) => {
                    break;
                }
                (Some(Ok(_)), None) => {
                    let pref = refs_sorted.next().expect("next").expect("no err");
                    num_written_lines += 1;
                    file.with_mut(|out| write_packed_ref(out, pref))?;
                }
                (Some(Ok(pref)), Some(edit)) => {
                    use std::cmp::Ordering::*;
                    match pref.name.as_bstr().cmp(edit.inner.name.as_bstr()) {
                        Less => {
                            let pref = refs_sorted.next().expect("next").expect("valid");
                            num_written_lines += 1;
                            file.with_mut(|out| write_packed_ref(out, pref))?;
                        }

View on GitHub (pinned to e73179060b)