{"record":{"id":"cec36750eec561dc","repo":"vectordotdev/vector","slug":"summary-must-not-be-empty","errorCode":null,"errorMessage":"`## Summary` must not be empty","messagePattern":"`## Summary` must not be empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vdev/src/commands/changelog/mod.rs","lineNumber":170,"sourceCode":"    {\n        bail!(\n            \"content between the title and `## Summary` is not allowed — move it into `## Summary` or `## Migration`.\"\n        );\n    }\n\n    let summary = after_title\n        .get(s_pos + SUMMARY_MARKER.len()..m_pos)\n        .unwrap_or(\"\")\n        .trim()\n        .to_string();\n    let migration = after_title\n        .get(m_pos + MIGRATION_MARKER.len()..)\n        .unwrap_or(\"\")\n        .trim()\n        .to_string();\n\n    if summary.is_empty() {\n        bail!(\"`## Summary` must not be empty\");\n    }\n\n    Ok(BreakingSections {\n        title,\n        anchor: anchor.map(str::to_string),\n        summary,\n        migration,\n    })\n}\n\n/// Split `\"Title Text {#anchor}\"` into `(\"Title Text\", Some(\"anchor\"))`.\n/// The `{#anchor}` suffix is a `Hugo`-style attribute list; `CommonMark` treats it as\n/// plain text so we peel it off here.\npub(crate) fn split_title_and_anchor(line: &str) -> (&str, Option<&str>) {\n    let trimmed = line.trim_end();\n    if let Some(prefix) = trimmed.strip_suffix('}')\n        && let Some(open) = prefix.rfind(\"{#\")\n    {","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/vdev/src/commands/changelog/mod.rs#L152-L188","documentation":"`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.","triggerScenarios":"Parsing a breaking fragment where the `## Summary` heading is present but has no body content before the `## Migration` heading (or only whitespace).","commonSituations":"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.","solutions":["Write a one-to-few-sentence description under `## Summary` describing the breaking change.","Re-run the changelog validation after filling it in."],"exampleFix":"// before\n# My change\n## Summary\n## Migration\nSteps.\n// after\n# My change\n## Summary\nThe `foo` option was renamed to `bar`.\n## Migration\nSteps.","handlingStrategy":"validation","validationCode":"let summary = text\n    .split(\"## Summary\").nth(1)\n    .and_then(|rest| rest.split(\"## Migration\").next())\n    .unwrap_or(\"\")\n    .trim();\nif summary.is_empty() {\n    eprintln!(\"'## Summary' section must not be empty\");\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = parse_breaking_sections(text) {\n    eprintln!(\"fill in sections: {e}\");\n}","preventionTips":["Fill in both template sections before committing the fragment.","Do not commit placeholder fragments with empty bodies.","Run the changelog validation command as a pre-commit habit."],"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"}