vectordotdev/vector · error

duplicate upgrade-guide anchor '#

Error message

duplicate upgrade-guide anchor '#{}' shared by breaking fragments '{other}' and '{}'. Override one with `{{#unique-slug}}`.

What it means

During breaking-fragment validation, anchors must be unique across the release's upgrade guide; `validate_breaking_anchors` keeps a HashMap of anchor-to-title and bails when two fragments map to the same anchor. Duplicate anchors would produce ambiguous markdown links in the generated upgrade guide.

Solutions

  1. Identify the two conflicting fragment titles from the message.
  2. Override one fragment's anchor with a unique slug, e.g. `{{#unique-slug}}` or a more descriptive name.
  3. Run `vdev check changelog-fragments` to confirm anchors are valid and unique.
  4. Re-run `vdev release generate-cue`.

Example fix

// before: two fragments both use {{#configuration}}
## new required config field {{#configuration}}
// after
## new required config field {{#new-required-config-field}}
Defensive patterns

Strategy: validation

Validate before calling

// detect duplicate anchors across changelog.d before release
const anchors = fragments.map(f => f.anchor);
const dupes = anchors.filter((a, i) => anchors.indexOf(a) !== i);
if (dupes.length) throw new Error(`duplicate anchors: ${dupes.join(", ")}`);

Try / catch

if let Err(e) = run() {
    if e.to_string().contains("duplicate upgrade-guide anchor") {
        eprintln!("rename one fragment's {{#slug}} to a unique value and re-run");
    }
}

Prevention

When it happens

Trigger: Two or more breaking changelog fragments in changelog.d declare the same `{{#slug}}` anchor (or derive identical slugs from their titles), and `vdev release generate-cue` runs validate_breaking_anchors.

Common situations: Two PRs independently picked the same intuitive slug like `{{#breaking-change}}` or `{{#configuration}}`, or a fragment was copied from an earlier one without renaming its anchor.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at vdev/src/commands/release/generate_cue.rs:751

    let version_slug = format!("{}-{}-{}", version.major, version.minor, version.patch);
    format!("{date}-{version_slug}-upgrade-guide.md")
}

/// Fail the release if any breaking fragment produced an invalid, empty, or duplicate
/// anchor. Uses the same anchor rules as `vdev check changelog-fragments` (shared through
/// `commands::changelog::is_valid_anchor`) so a fragment that passes CI can't fail here.
fn validate_breaking_anchors(breaking: &[&BreakingDetails]) -> Result<()> {
    let mut seen = std::collections::HashMap::<&str, &str>::new();
    for b in breaking {
        if !crate::commands::changelog::is_valid_anchor(&b.anchor) {
            bail!(
                "breaking fragment '{}' has an invalid anchor '{}'. Add `{{#some-valid-slug}}` after the title.",
                b.title,
                b.anchor,
            );
        }
        if let Some(other) = seen.insert(b.anchor.as_str(), b.title.as_str()) {
            bail!(
                "duplicate upgrade-guide anchor '#{}' shared by breaking fragments '{other}' and '{}'. Override one with `{{#unique-slug}}`.",
                b.anchor,
                b.title,
            );
        }
    }
    Ok(())
}

/// Write `content` to `path` via a `.tmp` sibling then atomic rename. Prevents leaving a
/// partial output behind if the process is killed mid-write.
fn atomic_write(path: &Path, content: &str) -> Result<()> {
    let tmp = path.with_extension(format!(
        "{}.tmp",
        path.extension().and_then(|e| e.to_str()).unwrap_or("")
    ));
    fs::write(&tmp, content).with_context(|| format!("Failed to write {}", tmp.display()))?;
    fs::rename(&tmp, path)

View on GitHub (pinned to 0d4ab78a4f)