vectordotdev/vector · error
invalid breaking fragment
Error message
invalid breaking fragment '{filename}': anchor must be a lowercase kebab-case slug (a-z, 0-9, hyphens); got '{anchor}'. What it means
`vdev check changelog-fragments` rejects a breaking fragment whose optional `anchor` is not a lowercase kebab-case slug (only a-z, 0-9 and hyphens). The validation is done by `is_valid_anchor` and the error echoes the offending anchor text.
Solutions
- Rewrite the anchor as lowercase kebab-case, e.g. `my-change-1234`.
- Remove the anchor entirely if it is not needed (anchors are optional).
- Re-run `make check-changelog-fragments`.
Example fix
# before anchor: My_Change # after anchor: my-change
Defensive patterns
Strategy: validation
Validate before calling
let anchor = "my-change"; assert!(anchor.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-'), "anchor must be kebab-case");
Type guard
fn is_kebab(s: &str) -> bool { !s.is_empty() && s.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-') } Prevention
- Use lowercase-with-hyphens whenever adding an anchor.
- Avoid underscores, spaces, and uppercase in anchors.
- Derive the anchor from the title with a slugify helper.
When it happens
Trigger: A breaking fragment file contains an anchor containing uppercase letters, spaces, underscores, or other characters outside `[a-z0-9-]`.
Common situations: Author writes `anchor: My_Change` or uses spaces instead of hyphens when giving the fragment a stable anchor for docs links.
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
- invalid breaking fragment
- invalid breaking fragment
- invalid fragment contents for
- invalid fragment contents for
- invalid fragment contents for
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/8a643fec6e12f739.
Report an issue: GitHub.
Appendix: source
Thrown at vdev/src/commands/check/changelog_fragments.rs:277
let body_before_authors = content
.rsplit_once("\nauthors: ")
.map_or(content, |(before, _)| before);
let sections = parse_breaking_sections(body_before_authors)
.map_err(|e| anyhow::anyhow!("invalid breaking fragment '{filename}': {e}"))?;
if sections.title.is_empty() {
bail!("invalid breaking fragment '{filename}': title must not be empty.");
}
if sections.title.contains("TODO") {
bail!(
"invalid breaking fragment '{filename}': title still contains the scaffolder placeholder 'TODO'."
);
}
if let Some(anchor) = sections.anchor.as_deref()
&& !is_valid_anchor(anchor)
{
bail!(
"invalid breaking fragment '{filename}': anchor must be a lowercase kebab-case slug (a-z, 0-9, hyphens); got '{anchor}'."
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use indoc::{formatdoc, indoc};
/// Build a full breaking-fragment file body from the given title line and section content.
fn wrap(title_line: &str, summary: &str, migration: &str) -> String {
formatdoc! {"
{title_line}
## SummaryView on GitHub (pinned to bdb87aeaa4)