{"record":{"id":"f52485ecc0e74532","repo":"vectordotdev/vector","slug":"h1-title-must-not-be-empty","errorCode":null,"errorMessage":"H1 title must not be empty","messagePattern":"H1 title must not be empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vdev/src/commands/changelog/mod.rs","lineNumber":129,"sourceCode":"pub(crate) fn parse_breaking_sections(body: &str) -> Result<BreakingSections> {\n    // Normalize CRLF → LF so Windows checkouts parse identically. Also pad the input so\n    // section markers like `\\n## Migration\\n` still match when the last section is empty\n    // and the file ends right after the header line.\n    let mut normalized = body.replace(\"\\r\\n\", \"\\n\");\n    if !normalized.ends_with('\\n') {\n        normalized.push('\\n');\n    }\n    let body: &str = &normalized;\n\n    // 1) File must begin with an H1 title on the first line — no leading blank lines.\n    let title_body = body\n        .strip_prefix(\"# \")\n        .ok_or_else(|| anyhow::anyhow!(\"first line must be an H1 title (`# ...`)\"))?;\n    let (title_line, after_title) = title_body.split_once('\\n').unwrap_or((title_body, \"\"));\n    let (title_text, anchor) = split_title_and_anchor(title_line);\n    let title = title_text.to_string();\n    if title.is_empty() {\n        bail!(\"H1 title must not be empty\");\n    }\n\n    // 2) Exactly one `## Summary` and one `## Migration`, in that order.\n    if after_title.matches(SUMMARY_MARKER).count() != 1 {\n        bail!(\"exactly one `## Summary` section is required\");\n    }\n    if after_title.matches(MIGRATION_MARKER).count() != 1 {\n        bail!(\"exactly one `## Migration` section is required\");\n    }\n    let s_pos = after_title.find(SUMMARY_MARKER).unwrap();\n    let m_pos = after_title.find(MIGRATION_MARKER).unwrap();\n    if s_pos >= m_pos {\n        bail!(\"`## Summary` must come before `## Migration`\");\n    }\n\n    // Reject any prose between the H1 title and `## Summary`. Someone hand-migrating an\n    // old free-form breaking fragment could carry over the previous body here and it\n    // would silently disappear from both the release CUE and the upgrade guide.","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/vdev/src/commands/changelog/mod.rs#L111-L147","documentation":"`parse_breaking_sections` validates breaking-change changelog fragments in vdev. After stripping the `# ` prefix, if the resulting H1 title text (with any anchor removed) is empty, it bails with \"H1 title must not be empty\". A fragment must start with a non-empty H1 heading because the title becomes the release entry title.","triggerScenarios":"Calling `parse_breaking_sections` (directly or via `changelog validate`/check commands) on a fragment whose first line is exactly `# `, `# #anchor`, or `#` followed by only whitespace/anchor, so the title text is empty.","commonSituations":"Hand-editing a `changelog.d/*.breaking.md` fragment and deleting the title text while keeping the heading; generating a fragment template and forgetting to fill in the title; moving an anchor without a title.","solutions":["Add descriptive text after `# ` on the first line of the breaking fragment.","Keep the anchor suffix if used, e.g. `# My Title {#my-anchor}`.","Run the vdev changelog validation command after editing to confirm the fragment parses."],"exampleFix":"// before (fragment first line)\n# {#breaking-anchor}\n// after\n# Rename config option X {#breaking-anchor}","handlingStrategy":"validation","validationCode":"fn has_h1_title(first_line: &str) -> bool {\n    first_line\n        .strip_prefix(\"# \")\n        .map(|rest| !rest.trim().is_empty())\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match parse_breaking_sections(text) {\n    Ok(sections) => render(sections),\n    Err(e) => eprintln!(\"fragment invalid: {e}; start title with '# <non-empty title>'\"),\n}","preventionTips":["Always scaffold breaking fragments with `vdev changelog new` instead of writing from scratch.","Never delete the title text while keeping the `# ` heading.","Run changelog validation locally before committing fragments."],"tags":["changelog","validation","markdown"],"backgroundTag":"empty-required-field","analyzedSha":"bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013","analyzedAt":"2026-09-16T02:53:35.741Z","contentChangedAt":"2026-09-16T02:53:35.741Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}