vectordotdev/vector · error
invalid breaking fragment
Error message
invalid breaking fragment '{name}': derived anchor '{anchor}' is not a valid kebab-case slug (add an explicit `{{#some-slug}}` after the title). What it means
Breaking-change fragments feed the upgrade guide, which anchors each section by a kebab-case slug. The checker parses the fragment's breaking sections, derives an anchor from the explicit `{{#anchor}}` or by slugifying the title, and bails when the derived anchor is not a valid kebab-case slug.
Solutions
- Add an explicit anchor after the section title, e.g. `## Upgrade notes {{#my-upgrade-note}}`, then re-run the check
- Or rewrite the title so it slugifies to valid kebab-case (lowercase letters, digits, hyphens)
- Check the derived anchor with the checker output and confirm `is_valid_anchor` accepts it (^[a-z0-9]+(-[a-z0-9]+)*$ pattern)
Example fix
// before
## Removes `--legacy-flag`!!!
// after
## Removes `--legacy-flag` {{#remove-legacy-flag}} Defensive patterns
Strategy: validation
Validate before calling
const anchor = explicitAnchor ?? slugify(title);
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(anchor)) throw new Error(`add explicit {{#anchor}} for '${title}'`); Type guard
const isValidKebab = (s) => /^[a-z0-9]+(-[a-z0-9]+)*$/.test(s);
Try / catch
try { runCheck(); } catch (e) { const m = /derived anchor '([^']+)'.*not a valid/.exec(e.message); if (m) addExplicitAnchorToFragment(m[1]); else throw e; } Prevention
- Add an explicit `{{#kebab-slug}}` to every breaking-change section title
- Keep breaking-section titles simple ASCII so slugification is predictable
- Run the fragments check locally before pushing breaking-change PRs
When it happens
Trigger: A `breaking` fragment whose title slugifies to something invalid (empty after slugification, or containing characters outside kebab-case) and which lacks an explicit `{{#some-slug}}` after the title, parsed by validate_breaking_anchor_set during the check.
Common situations: Titles made entirely of punctuation/emoji (slugify yields '' or junk); titles with slashes/underscores; copy-pasted Markdown headings without adding an explicit anchor.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- content between the title and `## Summary` is not allowed —…
- exactly one `## Migration` section is required
- exactly one `## Summary` section is required
- H1 title must not be empty
- `## Summary` must not be empty
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/4cda5d668ead3d9d.
Report an issue: GitHub.
Appendix: source
Thrown at vdev/src/commands/check/changelog_fragments.rs:132
if parts.len() != 3 || parts[2] != "md" {
continue;
}
let fragment_type = parts[1];
let Some(entry) = FRAGMENT_TYPES.iter().find(|t| t.name == fragment_type) else {
continue;
};
if !entry.breaking {
continue;
}
let content = std::fs::read_to_string(&path)?;
let body_before_authors = content
.rsplit_once("\nauthors: ")
.map_or(content.as_str(), |(before, _)| before);
let sections = parse_breaking_sections(body_before_authors)
.map_err(|e| anyhow::anyhow!("invalid breaking fragment '{name}': {e}"))?;
let anchor = sections.anchor.unwrap_or_else(|| slugify(§ions.title));
if !is_valid_anchor(&anchor) {
bail!(
"invalid breaking fragment '{name}': derived anchor '{anchor}' is not a valid kebab-case slug (add an explicit `{{#some-slug}}` after the title)."
);
}
if let Some(other) = seen.insert(anchor.clone(), name.clone()) {
bail!(
"duplicate upgrade-guide anchor '#{anchor}' shared by breaking fragments '{other}' and '{name}'. Override one with an explicit `{{#unique-slug}}`."
);
}
}
Ok(())
}
/// `git diff --name-only --diff-filter=<filter> --merge-base <merge_base> changelog.d`
///
/// `filter` is a `git diff` `--diff-filter` value: `A` for added-only, `M` for
/// modified-only, `AM` for both, etc.
fn diff_fragments(merge_base: &str, filter: &str) -> Result<Vec<PathBuf>> {
let filter_arg = format!("--diff-filter={filter}");View on GitHub (pinned to bdb87aeaa4)