GitoxideLabs/gitoxide · critical

if you see this the object database is correct as a delta…

Error message

if you see this the object database is correct as a delta refers to a non-existing object

What it means

This `unreachable!()` fires in the pack writer's `next_inner` (gix-pack `data/output/bytes.rs`) while writing a delta entry header: offsets of bases are only tracked for entries marked valid, and an invalid base slot means the delta referred to an object that does not exist in the object database. The authors assume counts were validated against the ODB beforehand, so reaching this indicates the object database is inconsistent (a delta whose base object is missing).

Solutions

  1. Run `git fsck --full` (or `gix`-based integrity checks) on the source repository to find and repair broken object chains.
  2. Ensure thin packs are disabled or that base objects are included when packing (verify the `Count` objects were fully resolved before streaming).
  3. Re-generate the pack from a healthy source (e.g. clone/fetch anew) instead of repacking the corrupted ODB.
  4. Report to gitoxide maintainers if the ODB is verified correct, since the code treats this state as impossible.
Defensive patterns

Strategy: validation

Validate before calling

// Verify object database integrity before packing
use std::process::Command;
let status = Command::new("git").args(["fsck", "--full"]).status()?;
if !status.success() {
    return Err(anyhow!("object database is corrupted; repair before packing"));
}

Try / catch

// Catch the panic at the pack-stream boundary
std::panic::catch_unwind(AssertUnwindSafe(|| pack_bytes_iter.next()))
    .map_err(|p| anyhow!("delta base missing during pack write: {:?}", p))?;

Prevention

When it happens

Trigger: Streaming a pack via the bytes output iterator when a DELTAFIED entry resolves its base through `pack_offsets_and_validity` and finds `is_valid_object == false` — i.e. the source ODB contained a delta referencing a non-existent (or pruned/corrupted) base object.

Common situations: Packing from a corrupted or manually-tampered object database, thin-pack assumptions violated (base objects not included), interrupted repacks that left dangling deltas, or bugs in custom ODB implementations that report objects as existing when their bases don't.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at gix-pack/src/data/output/bytes.rs:106

        if let Some((version, num_entries)) = self.header_info.take() {
            let header_bytes = crate::data::header::encode(version, num_entries);
            self.output
                .write_all(&header_bytes[..])
                .map_err(gix_hash::io::Error::from)?;
            self.written += header_bytes.len() as u64;
        }
        match self.input.next() {
            Some(entries) => {
                for entry in entries.map_err(Error::Input)? {
                    if entry.is_invalid() {
                        self.pack_offsets_and_validity.push((0, false));
                        continue;
                    }
                    self.pack_offsets_and_validity.push((self.written, true));
                    let header = entry.to_entry_header(self.entry_version, |index| {
                        let (base_offset, is_valid_object) = self.pack_offsets_and_validity[index];
                        if !is_valid_object {
                            unreachable!("if you see this the object database is correct as a delta refers to a non-existing object")
                        }
                        self.written - base_offset
                    });
                    self.written += header
                        .write_to(entry.decompressed_size as u64, &mut self.output)
                        .map_err(gix_hash::io::Error::from)? as u64;
                    self.written += std::io::copy(&mut &*entry.compressed_data, &mut self.output)
                        .map_err(gix_hash::io::Error::from)?;
                }
            }
            None => {
                let digest = self
                    .output
                    .hash
                    .clone()
                    .try_finalize()
                    .map_err(gix_hash::io::Error::from)?;
                self.output

View on GitHub (pinned to e73179060b)