zed-industries/zed · error

{:?} is in non-existent deleted hunk

Error message

{:?} is in non-existent deleted hunk

What it means

When a multibuffer renders a diff, deleted (base-only) hunks become DiffTransform::DeletedHunk entries whose text lives in the diff's base text. Computing a text summary for a range that intersects such a hunk requires that base text; if diff_state(buffer_id) no longer provides one, the code panics with '<range start> is in non-existent deleted hunk'. The snapshot still contains deleted-hunk geometry for a buffer whose diff base was dropped or replaced.

Source

Thrown at crates/multi_buffer/src/multi_buffer.rs:4637

    }

    pub fn max_row(&self) -> MultiBufferRow {
        MultiBufferRow(self.text_summary().lines.row)
    }

    pub fn text_summary(&self) -> MBTextSummary {
        self.diff_transforms.summary().output
    }

    pub fn text_summary_for_range<MBD, O>(&self, range: Range<O>) -> MBD
    where
        MBD: MultiBufferDimension + AddAssign,
        O: ToOffset,
    {
        let range = range.start.to_offset(self)..range.end.to_offset(self);
        let mut cursor = self
            .diff_transforms
            .cursor::<Dimensions<MultiBufferOffset, ExcerptOffset>>(());
        cursor.seek(&range.start, Bias::Right);

        let Some(first_transform) = cursor.item() else {
            return MBD::from_summary(&MBTextSummary::default());
        };

        let diff_transform_start = cursor.start().0;
        let diff_transform_end = cursor.end().0;
        let diff_start = range.start;
        let start_overshoot = diff_start - diff_transform_start;
        let end_overshoot = std::cmp::min(range.end, diff_transform_end) - diff_transform_start;

        let mut result = match first_transform {
            DiffTransform::BufferContent { .. } => {
                let excerpt_start = cursor.start().1 + start_overshoot;
                let excerpt_end = cursor.start().1 + end_overshoot;
                self.text_summary_for_excerpt_offset_range(excerpt_start..excerpt_end)
            }

View on GitHub (pinned to 5a9b9558db)

Solutions

  1. Obtain a new multibuffer snapshot after any diff/base-text change and recompute summaries from it
  2. Keep the diff base alive (retain the base buffer in the diff state) for as long as snapshots containing DeletedHunk transforms are in use
  3. When invalidating diffs, clear or replace the deleted-hunk transforms in the same edit so stale geometry cannot be queried
  4. If it reproduces, report it upstream with steps - this is an internal consistency invariant

Example fix

// before: summary computed on a snapshot whose diff base is gone
let summary = old_snapshot.text_summary_for_range(range); // panics inside DeletedHunk

// after: refresh the snapshot after diff invalidation, then compute
apply_diff_invalidation(&mut multi_buffer, cx);
let snapshot = multi_buffer.read(cx).snapshot(cx);
let summary = snapshot.text_summary_for_range(range);
Defensive patterns

Strategy: validation

Validate before calling

// never query summaries on a snapshot older than the last diff update:
// re-snapshot right before summarizing ranges that can cross deleted hunks
let snapshot = multi_buffer.read(cx).snapshot(cx);
let summary = snapshot.text_summary_for_range(range);

Prevention

When it happens

Trigger: Calling summary_for_range-style APIs (used by layout, scrolling, offset math) over a range crossing a DeletedHunk after the buffer's diff base was removed - the diff was invalidated, the base buffer closed, or the snapshot predates a diff rebuild that dropped base text.

Common situations: Races between git changes on disk and in-memory diff bases; closing the base/diff buffer while a snapshot with deleted hunks is still used for layout; partial updates that strip diff bases without recomputing the transforms that reference them.

Related errors


AI-assisted analysis of zed-industries/zed@5a9b9558db (2026-08-20). Data as JSON: /api/errors/afaca4a9e2c6e4c3. Report an issue: GitHub.