GitoxideLabs/gitoxide · error

pack entry header overflowed

Error message

pack entry header overflowed

What it means

While streaming-parsing a pack entry header, each continuation byte contributes 7 bits shifted left; if the shift exceeds 63 bits the `checked_shl` overflows and an InvalidData io::Error 'pack entry header overflowed' is returned. It means the multi-byte size header is implausibly long for a valid pack.

Solutions

  1. Verify the pack file integrity (`gix pack verify` / `git fsck`)
  2. Re-download or re-clone to replace the corrupt pack
  3. Confirm the source stream is an authentic git pack file
  4. During fuzzing/tests, treat this as correct rejection of invalid input
Defensive patterns

Strategy: try-catch

Validate before calling

// checksum the pack before parsing
assert_eq!(pack.trailer(), expected_sha);

Try / catch

match parse_result {
    Err(e) if e.to_string().contains("pack entry header overflowed") => {
        // reject pack as corrupt, re-fetch
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `data::Entry::from_read` (via streaming_parse_header_info) on a stream whose header has so many continuation bytes that the u64 shift would overflow — i.e. a corrupt or malicious header.

Common situations: Corrupted pack downloads, adversarial pack files, fuzzing inputs.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

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

    })
}

#[inline]
fn streaming_parse_header_info(read: &mut dyn io::Read) -> Result<(u8, u64, usize), io::Error> {
    let mut byte = [0u8; 1];
    read.read_exact(&mut byte)?;
    let mut c = byte[0];
    let mut i = 1;
    let type_id = (c >> 4) & 0b0000_0111;
    let mut size = u64::from(c) & 0b0000_1111;
    let mut shift = 4u32;
    while c & 0b1000_0000 != 0 {
        read.read_exact(&mut byte)?;
        c = byte[0];
        i += 1;
        let component = u64::from(c & 0b0111_1111)
            .checked_shl(shift)
            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "pack entry header overflowed"))?;
        size = size
            .checked_add(component)
            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "pack entry header overflowed"))?;
        shift += 7;
    }
    Ok((type_id, size, i))
}

/// Parses the header of a pack-entry, yielding object type id, decompressed object size, and consumed bytes
#[inline]
fn parse_header_info(data: &[u8]) -> Result<(u8, u64, usize), Error> {
    let mut c = *data.first().ok_or(Error::Corrupt {
        message: "need a pack entry header, got empty input",
    })?;
    let mut i = 1;
    let type_id = (c >> 4) & 0b0000_0111;
    let mut size = u64::from(c) & 0b0000_1111;
    let mut shift = 4u32;

View on GitHub (pinned to e73179060b)