vectordotdev/vector · error

`## Summary` must not be empty

Error message

`## Summary` must not be empty

What it means

`parse_breaking_sections` extracts the text between the `## Summary` and `## Migration` headings, trims it, and bails with "`## Summary` must not be empty" if nothing remains. Every breaking change must carry an actual summary, since it is rendered into the release notes.

Solutions

  1. Write a one-to-few-sentence description under `## Summary` describing the breaking change.
  2. Re-run the changelog validation after filling it in.

Example fix

// before
# My change
## Summary
## Migration
Steps.
// after
# My change
## Summary
The `foo` option was renamed to `bar`.
## Migration
Steps.
Defensive patterns

Strategy: validation

Validate before calling

let summary = text
    .split("## Summary").nth(1)
    .and_then(|rest| rest.split("## Migration").next())
    .unwrap_or("")
    .trim();
if summary.is_empty() {
    eprintln!("'## Summary' section must not be empty");
}

Try / catch

if let Err(e) = parse_breaking_sections(text) {
    eprintln!("fill in sections: {e}");
}

Prevention

When it happens

Trigger: Parsing a breaking fragment where the `## Summary` heading is present but has no body content before the `## Migration` heading (or only whitespace).

Common situations: Creating the fragment from the template and filling in Migration but not Summary; deleting Summary text during edits; an automated tooling bug that strips the section body.

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


AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16). Data as JSON: /api/errors/cec36750eec561dc. Report an issue: GitHub.

Appendix: source

Thrown at vdev/src/commands/changelog/mod.rs:170

    {
        bail!(
            "content between the title and `## Summary` is not allowed — move it into `## Summary` or `## Migration`."
        );
    }

    let summary = after_title
        .get(s_pos + SUMMARY_MARKER.len()..m_pos)
        .unwrap_or("")
        .trim()
        .to_string();
    let migration = after_title
        .get(m_pos + MIGRATION_MARKER.len()..)
        .unwrap_or("")
        .trim()
        .to_string();

    if summary.is_empty() {
        bail!("`## Summary` must not be empty");
    }

    Ok(BreakingSections {
        title,
        anchor: anchor.map(str::to_string),
        summary,
        migration,
    })
}

/// Split `"Title Text {#anchor}"` into `("Title Text", Some("anchor"))`.
/// The `{#anchor}` suffix is a `Hugo`-style attribute list; `CommonMark` treats it as
/// plain text so we peel it off here.
pub(crate) fn split_title_and_anchor(line: &str) -> (&str, Option<&str>) {
    let trimmed = line.trim_end();
    if let Some(prefix) = trimmed.strip_suffix('}')
        && let Some(open) = prefix.rfind("{#")
    {

View on GitHub (pinned to bdb87aeaa4)