{"record":{"id":"0781b5c6fa41bffe","repo":"gitbutlerapp/gitbutler","slug":"line-should-have-been-highlighted-by-now","errorCode":null,"errorMessage":"line should have been highlighted by now","messagePattern":"line should have been highlighted by now","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but/src/command/legacy/status/tui/details.rs","lineNumber":1001,"sourceCode":"                    if highlight_lines.is_none() {\n                        let syntax = line.syntax(syntax_set);\n                        *highlight_lines = Some(HighlightLines::new(syntax, syntax_theme));\n                    }\n\n                    let id = line.id;\n                    let mut strings = self.strings.lock();\n\n                    line.ensure_highlighted(\n                        syntax_set,\n                        highlight_lines.as_mut().unwrap(),\n                        self.theme,\n                        &mut strings,\n                    );\n\n                    let syntax_highlighted_line = line.syntax_highlighted_line.borrow();\n                    let syntax_highlighted_line = syntax_highlighted_line\n                        .as_ref()\n                        .expect(\"line should have been highlighted by now\");\n\n                    if self.section_is_to_be_discarded(id) {\n                        let crossed_out_line = syntax_highlighted_line\n                            .spans\n                            .iter()\n                            .cloned()\n                            .map(|span| span.crossed_out())\n                            .collect::<Line<'_>>()\n                            .style(syntax_highlighted_line.style);\n                        frame.render_widget(crossed_out_line, line_area);\n                    } else {\n                        frame.render_widget(syntax_highlighted_line, line_area);\n                    }\n\n                    if line\n                        .cli_id\n                        .as_ref()\n                        .is_some_and(|id| marks.contains_cli_id(id))","sourceCodeStart":983,"sourceCodeEnd":1019,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but/src/command/legacy/status/tui/details.rs#L983-L1019","documentation":"Internal invariant panic in the GitButler TUI details renderer. Each diff code line (DetailsCodeLine) lazily caches its syntax-highlighted ratatui Line in a RefCell (see crates/but/src/utils/diff_rendering.rs:257); ensure_highlighted() is called immediately before the expect to populate that cache. The panic means the line instance being rendered still has syntax_highlighted_line == None after that call, i.e. the call populated a different instance or was skipped for this line.","triggerScenarios":"Rendering the expanded details/diff pane (e.g. `but status` TUI with a section expanded) when the DetailsCodeLine being drawn is not the instance ensure_highlighted just filled: diff/line lists rebuilt mid-render (syntax set or theme swap, resize invalidation), or a refactor that drops/reorders the ensure_highlighted call for some line kinds. Note highlight_lines.as_mut().unwrap() just above can panic first if that Option is None.","commonSituations":"Regression after refactoring Details::render or ensure_highlighted; custom builds that change SyntaxSet/highlighting state while the TUI is running; first render after switching syntax-highlighting themes without rebuilding cached lines.","solutions":["Reproduce with RUST_BACKTRACE=1 in a debug build (`cargo run -p but -- status`) and compare the SectionId of the line passed to ensure_highlighted vs the one being borrowed","Make the invariant local: have ensure_highlighted return the cached Line (or a bool) instead of relying on a separate expect after the call","Re-run ensure_highlighted for the exact line right before borrowing, guarding against line-list replacement between the two steps","If an unmodified build panics, report upstream with the backtrace and the displayed section"],"exampleFix":"// before (details.rs)\nline.ensure_highlighted(syntax_set, highlight_lines.as_mut().unwrap(), self.theme, &mut strings);\nlet l = line.syntax_highlighted_line.borrow();\nlet l = l.as_ref().expect(\"line should have been highlighted by now\");\n\n// after — make ensure_highlighted return the populated cache entry\nlet highlighted = line.ensure_highlighted(syntax_set, hl, self.theme, &mut strings); // returns &Line<'static>\nframe.render_widget(highlighted, line_area);","handlingStrategy":"fallback","validationCode":"// inside a custom render loop, before drawing a code line:\nif line.syntax_highlighted_line.borrow().is_none() {\n    line.ensure_highlighted(syntax_set, highlight_lines, theme, &mut strings);\n}\ndebug_assert!(line.syntax_highlighted_line.borrow().is_some(), \"cache still empty for {:?}\", line.id);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep the ensure_highlighted call immediately adjacent to the borrow that reads the cache","Rebuild cached lines whenever SyntaxSet/theme/highlight state changes instead of reusing stale DetailsCodeLine instances","Run the details TUI in a subprocess and relaunch on crash — rendering panics should not take down the host process","Add a render test that expands every section kind so highlight-path regressions fail in CI"],"tags":["rust","ratatui","tui","syntax-highlighting","render","invariant"],"backgroundTag":"internal-invariant-panic","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}