zed-industries/zed · error

byte index {} is out of bounds of `{:?}` (length: {})

Error message

byte index {} is out of bounds of `{:?}` (length: {})

What it means

The rope crate stores text in chunks and mirrors Rust's string-slicing panics: when a byte offset exceeds the text's byte length, panic_char_boundary reports 'byte index ... is out of bounds (length: N)'. It is the rope equivalent of &s[i..] with i > s.len(), and almost always means an offset computed against one version of the text was applied to a shorter one.

Source

Thrown at crates/rope/src/chunk.rs:739

#[inline(always)]
fn nth_set_bit(v: u128, n: usize) -> usize {
    let low = v as u64;
    let high = (v >> 64) as u64;

    let low_count = low.count_ones() as usize;
    if n > low_count {
        64 + nth_set_bit_u64(high, (n - low_count) as u64) as usize
    } else {
        nth_set_bit_u64(low, n as u64) as usize
    }
}

#[cold]
#[inline(never)]
#[track_caller]
fn panic_char_boundary(text: &str, offset: usize) -> ! {
    if offset > text.len() {
        panic!(
            "byte index {} is out of bounds of `{:?}` (length: {})",
            offset,
            text,
            text.len()
        );
    }
    // find the character
    let char_start = text.floor_char_boundary(offset);
    // `char_start` must be less than len and a char boundary
    let ch = text.get(char_start..).unwrap().chars().next().unwrap();
    let char_range = char_start..char_start + ch.len_utf8();
    panic!(
        "byte index {} is not a char boundary; it is inside {:?} (bytes {:?})",
        offset, ch, char_range,
    );
}

#[cold]

View on GitHub (pinned to f4178619ac)

Solutions

  1. Recompute offsets from the current snapshot (to_offset on the up-to-date text) instead of reusing cached values
  2. Clamp before use: offset.min(text.len()) - and remember offset == len is valid as an exclusive end
  3. When walking forward n bytes, stop the walk at the end of the rope/chunk
  4. Add debug_assert!(offset <= len) at the boundary where offsets enter your code

Example fix

// before
let next = cached_offset + 8;
let slice = rope.byte_slice(next..); // panics when next > rope.len()

// after
let next = (cached_offset + 8).min(rope.len());
let slice = rope.byte_slice(next..);
Defensive patterns

Strategy: validation

Validate before calling

fn clamp_offset(offset: usize, len: usize) -> usize {
    offset.min(len)
}

let len = snapshot.len();
let start = clamp_offset(start, len);
let end = clamp_offset(end.max(start), len);

Prevention

When it happens

Trigger: Calling rope chunk APIs that validate byte offsets (char/byte iteration, slicing, summary lookups in crates/rope/src/chunk.rs) with an offset greater than the chunk length: stale offsets reused after edits, off-by-one at range ends (offset == len is a valid exclusive end, len + 1 is not), or arithmetic that walks past the end.

Common situations: Caching byte offsets across edits and reusing them; computing offset + n without clamping; mixing point/offset conversions from different snapshots; fuzz tests generating unclamped offsets.

Related errors


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