astral-sh/ruff · error

Annotation range `{bigger:?}` is beyond the end of buffer `{

Error message

Annotation range `{bigger:?}` is beyond the end of buffer `{source_len}`

What it means

SourceMap::annotated_lines maps annotation ranges onto source lines before rendering. It validates every annotation's span end against source.len(), tolerating at most one past the last character (so EOF spans can highlight an insertion point); anything larger panics with the offending range and buffer length. The panic means the spans were computed against different text than the source string handed to the renderer.

Source

Thrown at crates/ruff_annotate_snippets/src/renderer/source_map.rs:185

        lines
    }

    pub(crate) fn annotated_lines(
        &self,
        annotations: Vec<Annotation<'a>>,
        fold: bool,
    ) -> (usize, Vec<AnnotatedLineInfo<'a>>) {
        let source_len = self.source.len();
        if let Some(bigger) = annotations.iter().find_map(|x| {
            // Allow highlighting one past the last character in the source.
            if source_len + 1 < x.span.end {
                Some(&x.span)
            } else {
                None
            }
        }) {
            panic!("Annotation range `{bigger:?}` is beyond the end of buffer `{source_len}`")
        }

        let mut annotated_line_infos = self
            .lines
            .iter()
            .map(|info| AnnotatedLineInfo {
                line: info.line,
                line_index: info.line_index,
                annotations: vec![],
                keep: false,
            })
            .collect::<Vec<_>>();
        let mut multiline_annotations = vec![];

        for Annotation {
            span,
            label,
            kind,

View on GitHub (pinned to d1087a4b9e)

Solutions

  1. Derive both the source text and the annotation ranges from the same snapshot (e.g. the same SourceFile), so ends can never exceed its length.
  2. Clamp or drop annotations whose end exceeds source.len() + 1 before rendering.
  3. If diagnostics are rendered later than produced, invalidate or recompute spans on text-change notifications.

Example fix

// before: span from the full file, snippet built from a shorter string
let snippet = Snippet::source(&line_of_code)
    .annotation(AnnotationKind::Primary.span(full_file_range)); // end beyond buffer

// after: translate the range into the snippet's own coordinates
let snippet = Snippet::source(&line_of_code)
    .annotation(AnnotationKind::Primary.span(local_range)); // 0..line_of_code.len()
Defensive patterns

Strategy: validation

Validate before calling

fn annotations_within(source: &str, anns: &[Annotation<'_>]) -> bool {
    let len = source.len();
    anns.iter().all(|a| a.span.end <= len + 1 && a.span.start <= a.span.end)
}

// before rendering:
debug_assert!(annotations_within(snippet_source, annotations));

Try / catch

let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| renderer.render(&report)))
    .unwrap_or_else(|_| format!("{path}: {title}")); // fall back to a one-line diagnostic

Prevention

When it happens

Trigger: Calling the renderer with a Snippet whose source is shorter than an annotation's span end: reusing byte ranges from one file while passing a truncated or different source string; stale spans from before the file was edited; hardcoded offsets in tests against an updated fixture.

Common situations: Long-running processes (LSP servers) rendering with spans cached from before a file change; building context snippets from sliced source; tests with fixed numeric ranges that rot when fixtures change; off-by-one at EOF.

Related errors


AI-assisted analysis of astral-sh/ruff@d1087a4b9e (2026-08-20). Data as JSON: /api/errors/f8f2573e1f3539c0. Report an issue: GitHub.