{"record":{"id":"f8f2573e1f3539c0","repo":"astral-sh/ruff","slug":"annotation-range-bigger-is-beyond-the-end-of","errorCode":null,"errorMessage":"Annotation range `{bigger:?}` is beyond the end of buffer `{source_len}`","messagePattern":"Annotation range `(.+?)` is beyond the end of buffer `(.+?)`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ruff_annotate_snippets/src/renderer/source_map.rs","lineNumber":185,"sourceCode":"\n        lines\n    }\n\n    pub(crate) fn annotated_lines(\n        &self,\n        annotations: Vec<Annotation<'a>>,\n        fold: bool,\n    ) -> (usize, Vec<AnnotatedLineInfo<'a>>) {\n        let source_len = self.source.len();\n        if let Some(bigger) = annotations.iter().find_map(|x| {\n            // Allow highlighting one past the last character in the source.\n            if source_len + 1 < x.span.end {\n                Some(&x.span)\n            } else {\n                None\n            }\n        }) {\n            panic!(\"Annotation range `{bigger:?}` is beyond the end of buffer `{source_len}`\")\n        }\n\n        let mut annotated_line_infos = self\n            .lines\n            .iter()\n            .map(|info| AnnotatedLineInfo {\n                line: info.line,\n                line_index: info.line_index,\n                annotations: vec![],\n                keep: false,\n            })\n            .collect::<Vec<_>>();\n        let mut multiline_annotations = vec![];\n\n        for Annotation {\n            span,\n            label,\n            kind,","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/astral-sh/ruff/blob/d1087a4b9e03d253a88703f34e0869ee4b805456/crates/ruff_annotate_snippets/src/renderer/source_map.rs#L167-L203","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Clamp or drop annotations whose end exceeds source.len() + 1 before rendering.","If diagnostics are rendered later than produced, invalidate or recompute spans on text-change notifications."],"exampleFix":"// before: span from the full file, snippet built from a shorter string\nlet snippet = Snippet::source(&line_of_code)\n    .annotation(AnnotationKind::Primary.span(full_file_range)); // end beyond buffer\n\n// after: translate the range into the snippet's own coordinates\nlet snippet = Snippet::source(&line_of_code)\n    .annotation(AnnotationKind::Primary.span(local_range)); // 0..line_of_code.len()","handlingStrategy":"validation","validationCode":"fn annotations_within(source: &str, anns: &[Annotation<'_>]) -> bool {\n    let len = source.len();\n    anns.iter().all(|a| a.span.end <= len + 1 && a.span.start <= a.span.end)\n}\n\n// before rendering:\ndebug_assert!(annotations_within(snippet_source, annotations));","typeGuard":null,"tryCatchPattern":"let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| renderer.render(&report)))\n    .unwrap_or_else(|_| format!(\"{path}: {title}\")); // fall back to a one-line diagnostic","preventionTips":["Derive snippet source and annotation ranges from the same SourceFile snapshot.","In LSP/watchers, drop or recompute cached spans on text-change notifications.","Compute ranges with source_text.find(...) in tests instead of hardcoding offsets."],"tags":["diagnostics","rendering","panic","span","out-of-range"],"backgroundTag":"span-out-of-range","analyzedSha":"d1087a4b9e03d253a88703f34e0869ee4b805456","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}