GitoxideLabs/gitoxide · warning

in restore mode a hash is set

Error message

in restore mode a hash is set

What it means

A `expect()` panic in `try_read_trailer` of the pack entry iterator. In `input::Mode::Restore` the iterator must have been constructed with a hash to restore the pack trailer; `self.hash` is `None`, meaning the mode/hash pairing invariant was violated at construction time.

Solutions

  1. Always supply a `gix_hash::Kind`-derived hash when using `Mode::Restore`; use `Mode::Verify` or `Mode::Ignore` otherwise
  2. Construct iterators through the public `iterate_from`/`new` helpers instead of internal fields
  3. Update gix-pack and adapt to the current Mode API

Example fix

// before
let iter = BytesToEntriesIterator::new(data, Mode::Restore, None, path, id);
// after
let iter = BytesToEntriesIterator::new(data, Mode::Restore, Some(gix_hash::Kind::Sha1), path, id);
Defensive patterns

Strategy: validation

Validate before calling

// when constructing the iterator, only pass Mode::Restore together with a hash
let hash = if mode == input::Mode::Restore { Some(hash_kind) } else { None };

Try / catch

let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| iterator.next()));
if r.is_err() { eprintln!("Restore mode without hash"); }

Prevention

When it happens

Trigger: Using `gix_pack::data::input::BytesToEntriesIterator` (or `iterate_from`) with `input::Mode::Restore` where the hash was not supplied at construction — typically only via misusing internal constructors, not the public API which requires the hash for Restore mode.

Common situations: Writing custom pack streaming code that copies gix internals, or upgrading across versions where Mode constructors changed.

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/be08e5565d3946a4. Report an issue: GitHub.

Appendix: source

Thrown at gix-pack/src/data/input/bytes_to_entries.rs:190

        Ok(if self.objects_left == 0 {
            let mut id = gix_hash::ObjectId::null(self.object_hash);
            if let Err(err) = self.read.read_exact(id.as_mut_slice())
                && self.mode != input::Mode::Restore
            {
                return Err(input::Error::Io(err.into()));
            }

            if let Some(hash) = self.hash.take() {
                let actual_id = hash.try_finalize().map_err(gix_hash::io::Error::from)?;
                if self.mode == input::Mode::Restore {
                    id = actual_id;
                } else {
                    actual_id.verify(&id)?;
                }
            }
            Some(id)
        } else if self.mode == input::Mode::Restore {
            let hash = self.hash.clone().expect("in restore mode a hash is set");
            Some(hash.try_finalize().map_err(gix_hash::io::Error::from)?)
        } else {
            None
        })
    }
}

fn read_and_pass_to<R: io::Read, W: io::Write>(read: &mut R, to: W) -> PassThrough<&mut R, W> {
    PassThrough { read, write: to }
}

impl<R> Iterator for BytesToEntriesIter<R>
where
    R: io::BufRead,
{
    type Item = Result<input::Entry, input::Error>;

    fn next(&mut self) -> Option<Self::Item> {

View on GitHub (pinned to e73179060b)