rust-lang/cargo · error

multiple registries are configured with the same index url

Error message

multiple registries are configured with the same index url '{}': {}

What it means

Cargo resolves a registry `SourceId` back to a configured registry name (for credential lookup). It collects candidate names from env vars (`CARGO_REGISTRIES_<NAME>_INDEX`) and then from `[registries.*]` config. If two or more configured registries share the same canonical index URL, the mapping is ambiguous and Cargo bails, listing the colliding names.

Solutions

  1. Remove the duplicate registry definition so only one name maps to each index URL.
  2. If you need an alias, use `[source.<alias>] registry = "<canonical-name>"` (a source alias) instead of a second `[registries.<name>]` with the same index.
  3. Unset any `CARGO_REGISTRIES_<NAME>_INDEX` env var that duplicates a config entry's URL.
  4. Run `cargo config get registries` to list all configured registries and identify the collision.

Example fix

# before: two registries, same index
[registries.mirror]
index = "sparse+https://crates.io/index/"
[registries.upstream]
index = "sparse+https://crates.io/index/"

# after: alias via source, single registry
[registries.mirror]
index = "sparse+https://crates.io/index/"
[source.upstream]
registry = "mirror"
Defensive patterns

Strategy: validation

Validate before calling

fn registries_unique_indexes(config: &toml::Value) -> Result<(), anyhow::Error> {
    let mut seen = std::collections::HashSet::new();
    if let Some(regs) = config.get("registries").and_then(|v| v.as_table()) {
        for (name, v) in regs {
            if let Some(idx) = v.get("index").and_then(|s| s.as_str()) {
                if !seen.insert(idx.to_string()) {
                    anyhow::bail!("duplicate index across registries: {idx} (at {name})");
                }
            }
        }
    }
    Ok(())
}

Type guard

fn registry_indexes_unique(config: &toml::Value) -> bool {
    let mut s = std::collections::HashSet::new();
    config.get("registries").and_then(|v| v.as_table()).map_or(true, |t|
        t.values().filter_map(|v| v.get("index").and_then(|i| i.as_str())).all(|i| s.insert(i.to_string())))
}

Try / catch

match auth::registry_name_from_sid(&sid, gctx) {
    Ok(name) => Ok(name),
    Err(e) if e.to_string().contains("same index url") => {
        // guide user to dedupe; fall back to explicit --registry <name>
        Err(anyhow!("resolve the registry collision by passing --registry explicitly or removing the duplicate config"))
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: In `auth::registry_name_from_sid`, after gathering names whose index URL canonicalizes to the same value as the source's, `names.len() > 1`. Triggered by defining two `[registries.<name>]` entries (or env + config) with identical (or canonical-equivalent) `index` URLs.

Common situations: User aliases the same registry under two names (e.g. a corporate mirror and the original); leftover config from a rename where both old and new point at the same index; env var (`CARGO_REGISTRIES_X_INDEX`) duplicating a config-level entry pointing at the same URL.

Related errors


AI-assisted analysis of rust-lang/cargo@98a09e7e7d (2026-08-11). Data as JSON: /api/errors/1b07d3387be6d838. Report an issue: GitHub.

Appendix: source

Thrown at src/util/auth/mod.rs:193

        // Discover names from the configuration only if none were found in the environment.
        if names.len() == 0 {
            if let Some(registries) = gctx.values()?.get("registries") {
                let (registries, _) = registries.table("registries")?;
                for (name, value) in registries {
                    if let Some(v) = value.table(&format!("registries.{name}"))?.0.get("index") {
                        let (v, _) = v.string(&format!("registries.{name}.index"))?;
                        if index == &CanonicalUrl::new(&v.into_url()?)? {
                            names.push(name.clone());
                        }
                    }
                }
            }
        }
        names.sort();
        match names.len() {
            0 => None,
            1 => Some(std::mem::take(&mut names[0])),
            _ => anyhow::bail!(
                "multiple registries are configured with the same index url '{}': {}",
                &sid.as_url(),
                names.join(", ")
            ),
        }
    };

    // It's possible to have a registry configured in a Cargo config file,
    // then override it with configuration from environment variables.
    // If the name doesn't match, leave a note to help the user understand
    // the potentially confusing situation.
    if let Some(name) = name.as_deref() {
        if Some(name) != sid.alt_registry_key() {
            gctx.shell().note(format!(
                "name of alternative registry `{}` set to `{name}`",
                sid.url()
            ))?
        }

View on GitHub (pinned to 98a09e7e7d)