rust-lang/cargo · error

several matching instances of `target.'cfg(..)'.linker` in…

Error message

several matching instances of `target.'cfg(..)'.linker` in configurations
first match `{}` located in {}
second match `{}` located in {}

What it means

Symmetric to error 26 but for `linker`: two or more `[target.'cfg(...)'.linker]` entries match the current target's cfg set, so the linker is ambiguous and Cargo aborts rather than guess.

Solutions

  1. Consolidate to one config source or use exact-triple `target.<triple>.linker` keys.
  2. Disambiguate cfg predicates so only one matches the target.
  3. Remove the duplicate entry referenced by `first match`/`second match`.
  4. Audit all config layers with `cargo config get` to see merged sources.

Example fix

// before
# ~/.cargo/config.toml
[target.'cfg(target_env = "gnu")'.linker]
linker = "gcc"
# ./.cargo/config.toml
[target.'cfg(target_os = "linux")'.linker]
linker = "clang"

// after: keep only one
[target.x86_64-unknown-linux-gnu.linker]
linker = "clang"
Defensive patterns

Strategy: validation

Validate before calling

fn count_matching_linker_cfgs(target_cfg: &[String], cfgs: &[(String, Linker)]) -> usize {
    cfgs.iter().filter(|(k, _)| cfg_matches(k, target_cfg)).count()
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Layered config (global `~/.cargo/config.toml` + project `.cargo/config.toml` + `CARGO_TARGET_*_LINKER`) where cfg-based linker entries both match; refactoring cfg predicates into overlapping ranges.

Common situations: Teams share a global config that sets a linker, then a project config also sets one for a broader cfg; CI sets `CARGO_TARGET_CFG_GNU_LINKER` while config file also covers it.

Related errors


AI-assisted analysis of rust-lang/cargo@42eee92bc9 (2026-08-11). Data as JSON: /api/errors/b0da7054d6d14665. Report an issue: GitHub.

Appendix: source

Thrown at src/compiler/compilation.rs:591

    // With `target-applies-to-host = true`,
    // host artifacts must fall through to pick up from [target]
    // since this is the stable behavior
    if kind.is_host() && !bcx.gctx.target_applies_to_host()? {
        return Ok(None);
    }

    // Try target.'cfg(...)'.linker.
    let target_cfg = bcx.target_data.info(kind).cfg();
    let mut cfgs = bcx
        .gctx
        .target_cfgs()?
        .iter()
        .filter_map(|(key, cfg)| cfg.linker.as_ref().map(|linker| (key, linker)))
        .filter(|(key, _linker)| CfgExpr::matches_key(key, target_cfg));
    let matching_linker = cfgs.next();
    if let Some((key, linker)) = cfgs.next() {
        anyhow::bail!(
            "several matching instances of `target.'cfg(..)'.linker` in configurations\n\
             first match `{}` located in {}\n\
             second match `{}` located in {}",
            matching_linker.unwrap().0,
            matching_linker.unwrap().1.definition,
            key,
            linker.definition
        );
    }
    Ok(matching_linker.map(|(_k, linker)| linker.val.clone().resolve_program(bcx.gctx)))
}

fn explicit_host_kind(host: &str) -> CompileKind {
    let target = CompileTarget::new(host, false).expect("must be a host tuple");
    CompileKind::Target(target)
}

View on GitHub (pinned to 42eee92bc9)