GitoxideLabs/gitoxide · warning

a non-delta entry

Error message

a non-delta entry

What it means

This is a Rust `expect()` panic, not a recoverable error. `decode_entry` decompresses a pack entry that was matched as a non-delta kind (Tree/Blob/Commit/Tag) and then calls `entry.header.as_kind()`, which returns `None` only for delta headers. By construction this branch can only hold a non-delta header, so the expect encodes an internal invariant; failure means a logic bug in gix-pack.

Solutions

  1. Update gix-pack to the latest patch release in case the invariant was fixed
  2. Do not construct or decode pack entries with hand-modified headers; use intact packs
  3. If reproducible, file a bug with the pack file and the call stack
Defensive patterns

Strategy: try-catch

Try / catch

let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| pack.decode_entry(id, &mut out, ...)));
match outcome {
    Ok(res) => res, // Result already conveys decode errors
    Err(_) => eprintln!("gix-pack internal panic while decoding entry"),
}

Prevention

When it happens

Trigger: Calling `gix_pack::data::File::decode_entry` on a non-delta entry; the panic fires only if the match arm guarding this code ever admits an `OfsDelta`/`RefDelta` header, i.e. due to a library bug or modified/internal code paths.

Common situations: Practically never hit by library users; seen during gix-pack development, when patching the decoder, or when fuzzing mutated pack files through decode_entry.

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

Appendix: source

Thrown at gix-pack/src/data/file/decode/entry.rs:207

        &self,
        entry: data::Entry,
        out: &mut Vec<u8>,
        inflate: &mut gix_zlib::Inflate,
        resolve: &dyn Fn(&gix_hash::oid, &mut Vec<u8>) -> Option<ResolvedBase>,
        delta_cache: &mut dyn cache::DecodeEntry,
    ) -> Result<Outcome, Error> {
        use crate::data::entry::Header::*;
        match entry.header {
            Tree | Blob | Commit | Tag => {
                let size = self.decoded_object_size(entry.decompressed_size)?;
                if let Some(additional) = size.checked_sub(out.len()) {
                    out.try_reserve(additional)?;
                }
                out.resize(size, 0);
                self.decompress_entry(&entry, inflate, out.as_mut_slice())
                    .map(|consumed_input| {
                        Outcome::from_object_entry(
                            entry.header.as_kind().expect("a non-delta entry"),
                            &entry,
                            consumed_input,
                        )
                    })
            }
            OfsDelta { .. } | RefDelta { .. } => self.resolve_deltas(entry, resolve, inflate, out, delta_cache),
        }
    }

    /// resolve: technically, this shouldn't ever be required as stored local packs don't refer to objects by id
    /// that are outside of the pack. Unless, of course, the ref refers to an object within this pack, which means
    /// it's very, very large as 20bytes are smaller than the corresponding MSB encoded number
    fn resolve_deltas(
        &self,
        last: data::Entry,
        resolve: &dyn Fn(&gix_hash::oid, &mut Vec<u8>) -> Option<ResolvedBase>,
        inflate: &mut gix_zlib::Inflate,
        out: &mut Vec<u8>,

View on GitHub (pinned to e73179060b)