{"record":{"id":"8f9a2f47f80a15d7","repo":"vectordotdev/vector","slug":"duplicate-upgrade-guide-anchor-anchor-shared-by-breaking","errorCode":null,"errorMessage":"duplicate upgrade-guide anchor '#{anchor}' shared by breaking fragments '{other}' and '{name}'. Override one with an explicit `{{#unique-slug}}`.","messagePattern":"duplicate upgrade-guide anchor '#(.+?)' shared by breaking fragments '(.+?)' and '(.+?)'\\. Override one with an explicit `(.+?)\\}`\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vdev/src/commands/check/changelog_fragments.rs","lineNumber":137,"sourceCode":"            continue;\n        };\n        if !entry.breaking {\n            continue;\n        }\n        let content = std::fs::read_to_string(&path)?;\n        let body_before_authors = content\n            .rsplit_once(\"\\nauthors: \")\n            .map_or(content.as_str(), |(before, _)| before);\n        let sections = parse_breaking_sections(body_before_authors)\n            .map_err(|e| anyhow::anyhow!(\"invalid breaking fragment '{name}': {e}\"))?;\n        let anchor = sections.anchor.unwrap_or_else(|| slugify(&sections.title));\n        if !is_valid_anchor(&anchor) {\n            bail!(\n                \"invalid breaking fragment '{name}': derived anchor '{anchor}' is not a valid kebab-case slug (add an explicit `{{#some-slug}}` after the title).\"\n            );\n        }\n        if let Some(other) = seen.insert(anchor.clone(), name.clone()) {\n            bail!(\n                \"duplicate upgrade-guide anchor '#{anchor}' shared by breaking fragments '{other}' and '{name}'. Override one with an explicit `{{#unique-slug}}`.\"\n            );\n        }\n    }\n    Ok(())\n}\n\n/// `git diff --name-only --diff-filter=<filter> --merge-base <merge_base> changelog.d`\n///\n/// `filter` is a `git diff` `--diff-filter` value: `A` for added-only, `M` for\n/// modified-only, `AM` for both, etc.\nfn diff_fragments(merge_base: &str, filter: &str) -> Result<Vec<PathBuf>> {\n    let filter_arg = format!(\"--diff-filter={filter}\");\n    let out = git::run_and_check_output(&[\n        \"diff\",\n        \"--name-only\",\n        &filter_arg,\n        \"--merge-base\",","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/vectordotdev/vector/blob/bdb87aeaa4c4ff27c0ba643c1c77b21bf2ef4013/vdev/src/commands/check/changelog_fragments.rs#L119-L155","documentation":"The changelog check found two breaking-change fragments whose upgrade-guide headings derive the same Markdown anchor slug. Ambiguous anchors break deep links in the rendered upgrade guide. Fix by renaming one heading or adding an explicit `{{#unique-slug}}` override after the title.","triggerScenarios":"Running `vdev check changelog-fragments` (via exec) when two `*.breaking.md` fragments contain headings that normalize to the identical kebab-case anchor, and neither has an explicit `{{#slug}}` override.","commonSituations":"Two contributors independently add breaking-change fragments with identical titles like 'Remove deprecated option' before merging; rebasing multiple PRs each adding the same section title into the same release's changelog.d directory.","solutions":["Rename the heading in one of the two conflicting breaking fragments so the derived anchors differ.","Add an explicit anchor override `{{#some-unique-slug}}` after the title of one fragment's heading.","If the duplicate came from a merge/rebase, consolidate the two fragments into a single breaking fragment."],"exampleFix":"// before: two fragments both titled\n## Remove deprecated config option\n// after\n## Remove deprecated config option {{#remove-deprecated-config-option-sinks}}","handlingStrategy":"validation","validationCode":"let anchors: Vec<String> = std::fs::read_dir(\"changelog.d\")?.filter_map(|e| e.ok())\n    .filter(|e| e.path().extension().map_or(false, |x| x == \"md\"))\n    .filter_map(|e| std::fs::read_to_string(e.path()).ok())\n    .filter_map(|c| c.lines().find(|l| l.starts_with(\"## \")).map(|h| slugify(h)))\n    .collect();\nlet dupes: Vec<_> = anchors.iter().duplicates().collect();\nassert!(dupes.is_empty(), \"duplicate upgrade-guide anchors: {dupes:?}\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Give each breaking fragment a distinctive, feature-specific heading.","Add an explicit `{{#slug}}` override whenever the title may be duplicated.","Run `vdev check changelog-fragments` locally before pushing."],"tags":["rust","changelog","ci","documentation"],"backgroundTag":"invalid-identifier-format","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"}