vectordotdev/vector · error
H1 title must not be empty
Error message
H1 title must not be empty
What it means
`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.
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.
Example fix
// before (fragment first line)
# {#breaking-anchor}
// after
# Rename config option X {#breaking-anchor} Defensive patterns
Strategy: validation
Validate before calling
fn has_h1_title(first_line: &str) -> bool {
first_line
.strip_prefix("# ")
.map(|rest| !rest.trim().is_empty())
.unwrap_or(false)
} Try / catch
match parse_breaking_sections(text) {
Ok(sections) => render(sections),
Err(e) => eprintln!("fragment invalid: {e}; start title with '# <non-empty title>'"),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- content between the title and `## Summary` is not allowed —…
- exactly one `## Migration` section is required
- exactly one `## Summary` section is required
- invalid breaking fragment
- `## Summary` must not be empty
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/f52485ecc0e74532.
Report an issue: GitHub.
Appendix: source
Thrown at vdev/src/commands/changelog/mod.rs:129
pub(crate) fn parse_breaking_sections(body: &str) -> Result<BreakingSections> {
// Normalize CRLF → LF so Windows checkouts parse identically. Also pad the input so
// section markers like `\n## Migration\n` still match when the last section is empty
// and the file ends right after the header line.
let mut normalized = body.replace("\r\n", "\n");
if !normalized.ends_with('\n') {
normalized.push('\n');
}
let body: &str = &normalized;
// 1) File must begin with an H1 title on the first line — no leading blank lines.
let title_body = body
.strip_prefix("# ")
.ok_or_else(|| anyhow::anyhow!("first line must be an H1 title (`# ...`)"))?;
let (title_line, after_title) = title_body.split_once('\n').unwrap_or((title_body, ""));
let (title_text, anchor) = split_title_and_anchor(title_line);
let title = title_text.to_string();
if title.is_empty() {
bail!("H1 title must not be empty");
}
// 2) Exactly one `## Summary` and one `## Migration`, in that order.
if after_title.matches(SUMMARY_MARKER).count() != 1 {
bail!("exactly one `## Summary` section is required");
}
if after_title.matches(MIGRATION_MARKER).count() != 1 {
bail!("exactly one `## Migration` section is required");
}
let s_pos = after_title.find(SUMMARY_MARKER).unwrap();
let m_pos = after_title.find(MIGRATION_MARKER).unwrap();
if s_pos >= m_pos {
bail!("`## Summary` must come before `## Migration`");
}
// Reject any prose between the H1 title and `## Summary`. Someone hand-migrating an
// old free-form breaking fragment could carry over the previous body here and it
// would silently disappear from both the release CUE and the upgrade guide.View on GitHub (pinned to bdb87aeaa4)