crates/gunnar-pack/src/error.rs
//! The crate's error type.
/// The result type used throughout this crate.
pub type Result<T, E = Error> = std::result::Result<T, E>;
/// Everything that can go wrong while writing or indexing a pack.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
/// An object that was asked for is not in the object source.
///
/// For `upload-pack` this is the "not our ref" case and must be reported to the
/// client rather than producing a short pack.
#[error("object {id} is not present in the object source")]
ObjectMissing {
/// The id that could not be found.
id: gix_hash::ObjectId,
},
/// The object source failed while looking an object up.
#[error("looking up object {id} failed")]
Find {
/// The id whose lookup failed.
id: gix_hash::ObjectId,
/// The underlying failure. gix reports object-access failures as
/// `gix_error` exceptions since gix-object 0.65 (gix 0.88); this is that
/// exception converted into an `std::error::Error`.
source: gix_error::Error,
},
/// An object id does not use the hash kind the pack is being written with.
///
/// Mixing hash kinds silently is how a sha256 repository ends up being served a
/// sha1 answer, which yields an empty response and no error at all.
#[error("object id {id} is {actual:?}, but this pack is being written as {expected:?}")]
HashKindMismatch {
/// The offending id.
id: gix_hash::ObjectId,
/// The kind the id actually has.
actual: gix_hash::Kind,
/// The kind the pack declared.
expected: gix_hash::Kind,
},
/// More objects were handed to a [`PackWriter`](crate::PackWriter) than its header
/// declared. The count is in the header and cannot be revised afterwards.
#[error("the pack header declared {declared} objects, cannot write another one")]
PackFull {
/// The count written into the pack header.
declared: u32,
},
/// A [`PackWriter`](crate::PackWriter) was finished before its declared number of
/// objects had been written, which would produce a truncated, invalid pack.
#[error("the pack header declared {declared} objects but only {written} were written")]
ObjectCountMismatch {
/// The count written into the pack header.
declared: u32,
/// How many objects actually made it in.
written: u32,
},
/// A pack's object count is a `u32`.
#[error("a pack can hold at most 4294967295 objects, was given {0}")]
TooManyObjects(usize),
/// The pack-copy generator failed while producing entries.
#[error("failed to produce pack entries")]
Generate(#[source] gix_error::Error),
/// An object the pack was to contain could not be located while it was
/// being generated.
///
/// [`write_pack`](crate::write_pack)'s [`Error::ObjectMissing`] with the id
/// dropped: gix's generator marks an object it could not find with a null
/// id, so there is nothing honest left to name. The pack is abandoned
/// rather than shortened either way.
#[error("an object vanished from the store while its pack was being generated")]
ObjectVanished,
/// Writing pack bytes failed.
#[error("failed to write pack data")]
Io(#[from] std::io::Error),
/// Computing the pack's trailing checksum failed, which for SHA-1 means a
/// collision attack was detected.
#[error("failed to compute the pack checksum")]
Hasher(#[source] gix_error::Error),
/// The delta encoder refused or failed.
///
/// Chiefly [`gunnar_delta::Error::Refused`], which names the parameter, the
/// value and the cap it exceeded. A refusal is deliberate and is not
/// downgraded into a whole-object pack here: a caller that asked for a
/// window of a million objects should learn that it did not get one.
#[cfg(feature = "delta")]
#[error("computing pack deltas failed")]
Delta(#[from] gunnar_delta::Error),
/// gix refused to index the pack.
///
/// This is the receive-pack rejection path: a corrupt, truncated or
/// unresolvable-thin pack lands here.
#[error("failed to index the pack")]
Index(#[source] gix_error::Error),
}
// No `#[from]` on the gix-backed variants (2026-10-02, gix 0.88): since
// gix-pack 0.75 / gix-hash 0.27 every one of them reports a `gix_error::Exn`
// (a `gix_error::Error` since gix-pack 0.77 / gix 0.90)
// instead of its own error enum, so the generator, the hasher and the indexer
// would all convert from the SAME type and a blanket `From` could only pick one
// meaning. Each call site names the variant instead.