GitoxideLabs/gitoxide · error

BUG: hunks are never empty

Error message

BUG: hunks are never empty

What it means

force_non_zero converts a u32 hunk count/length to NonZeroU32 and panics if the value is 0. The library asserts hunks are never empty (length 0), so a zero-length hunk reaching this function is an invariant violation in the blame computation rather than a user-facing error.

Solutions

  1. Check why a zero-length/empty hunk set is being produced and filter or reject empty hunks before conversion
  2. Replace the expect with NonZeroU32::new(n).ok_or(...)? or an explicit error to surface the condition to callers
  3. Verify the blame inputs (file/commit) are valid and produce non-empty hunks
  4. Update gix-blame to a version where zero-length hunks are handled

Example fix

// before
fn force_non_zero(n: u32) -> NonZeroU32 {
    NonZeroU32::new(n).expect("BUG: hunks are never empty")
}
// after
fn force_non_zero(n: u32) -> Option<NonZeroU32> {
    NonZeroU32::new(n)
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate before requesting stats/output:
if hunk_count == 0 { return Err(anyhow!("no hunks produced; cannot build non-zero output")); }

Type guard

fn is_non_zero(n: u32) -> Option<NonZeroU32> { NonZeroU32::new(n) }

Prevention

When it happens

Trigger: Calling APIs that build FileStats/HunkOutputs (e.g. blame statistics output) where the computed number of hunks or hunk length is 0, violating the 'hunks are never empty' invariant.

Common situations: Seen after changes in blame algorithms that can produce zero-length hunks, or when blaming files/commits in edge cases (empty diffs) that were assumed impossible.

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

Appendix: source

Thrown at gix-blame/src/file/mod.rs:456

}

impl BlameEntry {
    /// Create an offset from a portion of the *Blamed File*.
    fn from_unblamed_hunk(unblamed_hunk: &UnblamedHunk, commit_id: ObjectId) -> Option<Self> {
        let range_in_source_file = unblamed_hunk.get_range(&commit_id)?;

        Some(Self {
            start_in_blamed_file: unblamed_hunk.range_in_blamed_file.start,
            start_in_source_file: range_in_source_file.start,
            len: force_non_zero(range_in_source_file.len() as u32),
            commit_id,
            source_file_name: unblamed_hunk.source_file_name.clone(),
        })
    }
}

fn force_non_zero(n: u32) -> NonZeroU32 {
    NonZeroU32::new(n).expect("BUG: hunks are never empty")
}

#[cfg(test)]
mod tests;

View on GitHub (pinned to e73179060b)