{"record":{"id":"06bd552ada9558e6","repo":"zed-industries/zed","slug":"byte-index-is-out-of-bounds-of-length","errorCode":null,"errorMessage":"byte index {} is out of bounds of `{:?}` (length: {})","messagePattern":"byte index (.+?) is out of bounds of `(.+?)` \\(length: (.+?)\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/rope/src/chunk.rs","lineNumber":739,"sourceCode":"#[inline(always)]\nfn nth_set_bit(v: u128, n: usize) -> usize {\n    let low = v as u64;\n    let high = (v >> 64) as u64;\n\n    let low_count = low.count_ones() as usize;\n    if n > low_count {\n        64 + nth_set_bit_u64(high, (n - low_count) as u64) as usize\n    } else {\n        nth_set_bit_u64(low, n as u64) as usize\n    }\n}\n\n#[cold]\n#[inline(never)]\n#[track_caller]\nfn panic_char_boundary(text: &str, offset: usize) -> ! {\n    if offset > text.len() {\n        panic!(\n            \"byte index {} is out of bounds of `{:?}` (length: {})\",\n            offset,\n            text,\n            text.len()\n        );\n    }\n    // find the character\n    let char_start = text.floor_char_boundary(offset);\n    // `char_start` must be less than len and a char boundary\n    let ch = text.get(char_start..).unwrap().chars().next().unwrap();\n    let char_range = char_start..char_start + ch.len_utf8();\n    panic!(\n        \"byte index {} is not a char boundary; it is inside {:?} (bytes {:?})\",\n        offset, ch, char_range,\n    );\n}\n\n#[cold]","sourceCodeStart":721,"sourceCodeEnd":757,"githubUrl":"https://github.com/zed-industries/zed/blob/f4178619acd0d47ea1f76a2025c42962c6d6638c/crates/rope/src/chunk.rs#L721-L757","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Recompute offsets from the current snapshot (to_offset on the up-to-date text) instead of reusing cached values","Clamp before use: offset.min(text.len()) - and remember offset == len is valid as an exclusive end","When walking forward n bytes, stop the walk at the end of the rope/chunk","Add debug_assert!(offset <= len) at the boundary where offsets enter your code"],"exampleFix":"// before\nlet next = cached_offset + 8;\nlet slice = rope.byte_slice(next..); // panics when next > rope.len()\n\n// after\nlet next = (cached_offset + 8).min(rope.len());\nlet slice = rope.byte_slice(next..);","handlingStrategy":"validation","validationCode":"fn clamp_offset(offset: usize, len: usize) -> usize {\n    offset.min(len)\n}\n\nlet len = snapshot.len();\nlet start = clamp_offset(start, len);\nlet end = clamp_offset(end.max(start), len);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never cache byte offsets across edits; recompute them from the live snapshot","Clamp at every arithmetic site: offset + n can exceed len","Fuzz with property tests asserting offsets stay within 0..=len","Prefer Point- or Anchor-based APIs over raw byte arithmetic"],"tags":["rope","text","byte-offset","out-of-bounds","panic"],"backgroundTag":"index-out-of-bounds","analyzedSha":"f4178619acd0d47ea1f76a2025c42962c6d6638c","analyzedAt":"2026-08-20T19:29:52.058Z","contentChangedAt":"2026-08-20T19:29:52.058Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}