{"record":{"id":"f5e142987e09bf18","repo":"astral-sh/ruff","slug":"expected-a-title","errorCode":null,"errorMessage":"Expected a Title","messagePattern":"Expected a Title","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ruff_annotate_snippets/src/renderer/render.rs","lineNumber":244,"sourceCode":"            }\n            buffer\n                .render(&level, &renderer.stylesheet, &mut out_string)\n                .unwrap();\n            if g != group_len - 1 {\n                out_string.push('\\n');\n            }\n        }\n        out_string\n    }\n}\n\nfn render_short_message(renderer: &Renderer, groups: &[Group<'_>]) -> Result<String, fmt::Error> {\n    let mut buffer = StyledBuffer::new();\n    let mut labels = None;\n    let group = groups.first().expect(\"Expected at least one group\");\n\n    let Some(title) = &group.title else {\n        panic!(\"Expected a Title\");\n    };\n\n    if let Some(Element::Cause(cause)) = group\n        .elements\n        .iter()\n        .find(|e| matches!(e, Element::Cause(_)))\n    {\n        let labels_inner = cause\n            .markers\n            .iter()\n            .filter_map(|ann| match &ann.label {\n                Some(msg) if ann.kind.is_primary() => {\n                    if !msg.trim().is_empty() {\n                        Some(msg.to_string())\n                    } else {\n                        None\n                    }\n                }","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/astral-sh/ruff/blob/d1087a4b9e03d253a88703f34e0869ee4b805456/crates/ruff_annotate_snippets/src/renderer/render.rs#L226-L262","documentation":"ruff_annotate_snippets is the vendored annotate-snippets renderer used for ruff/ty diagnostics. When a Renderer is built with .short_message(true), Renderer::render() delegates to render_short_message(), which emits rustc-style one-line messages ('path:line:col: level[id]: title'). That path requires the first group to carry a title (set via Level::primary_title()/secondary_title()); a report built without one violates the invariant and panics with 'Expected a Title'.","triggerScenarios":"Calling Renderer::plain().short_message(true).render(...) with a Level whose group has no title — e.g. a new diagnostic wired into concise/short rendering that sets only .element(Snippet...) annotations and never .primary_title(...).","commonSituations":"Adding a new diagnostic and forgetting the title; refactoring titles to be optional in shared rendering code; writing snapshot tests for the short format without a title.","solutions":["Attach a title to the Level before adding elements: Level::ERROR.primary_title(\"...\") (or secondary_title).","If no title exists, render with the normal (non-short) renderer instead of short_message(true).","Add a unit test that renders the diagnostic with a short_message(true) renderer so the invariant is enforced in CI."],"exampleFix":"// before: no title on the Level -> panic: Expected a Title\nlet input = &[Level::ERROR\n    .element(Snippet::source(src)\n        .annotation(AnnotationKind::Primary.span(0..5)))];\nlet out = Renderer::plain().short_message(true).render(input);\n\n// after\nlet input = &[Level::ERROR\n    .primary_title(\"E501 line too long\")\n    .element(Snippet::source(src)\n        .annotation(AnnotationKind::Primary.span(0..5)))];\nlet out = Renderer::plain().short_message(true).render(input);","handlingStrategy":"validation","validationCode":"// Lock the invariant with a test that renders every diagnostic in short mode.\n#[test]\nfn diagnostics_render_short_message() {\n    for report in all_diagnostic_reports() { // each report must carry a title\n        let _ = Renderer::plain().short_message(true).render(report);\n    }\n}","typeGuard":null,"tryCatchPattern":"let rendered = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    renderer.render(&report)\n}))\n.unwrap_or_else(|_| report.title_only_fallback());","preventionTips":["Treat a title as required whenever the renderer runs in short_message mode; set it in the same builder chain as the first element.","Cover every diagnostic with a concise/short render test so a missing title fails CI, not users.","When making titles optional, switch affected paths to the full renderer explicitly."],"tags":["diagnostics","rendering","panic","title","annotate-snippets"],"backgroundTag":"missing-render-title","analyzedSha":"d1087a4b9e03d253a88703f34e0869ee4b805456","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}