rust-lang/cargo · error

found build scripts with duplicate file stems, but all…

Error message

found build scripts with duplicate file stems, but all build scripts must have a unique file stem

What it means

When `package.build` lists multiple build scripts (`MultipleScript`), each must have a unique file stem because Cargo derives the generated target name as `build-script-<stem>`. `validate_unique_build_scripts` groups paths by stem and bails if any stem maps to more than one path, listing the offenders.

Solutions

  1. Give each build script a distinct file name, e.g. `a/a_build.rs` and `b/b_build.rs`.
  2. Consolidate the logic of duplicate-stem scripts into a single build script.
  3. Re-run `cargo check` to confirm stems are unique.

Example fix

# before
[package]
build = ["a/build.rs", "b/build.rs"]

# after
[package]
build = ["a/a_build.rs", "b/b_build.rs"]
Defensive patterns

Strategy: validation

Validate before calling

use std::collections::HashMap;
fn build_scripts_unique_stems(scripts: &[String]) -> bool {
    let mut stems: HashMap<String, usize> = HashMap::new();
    for s in scripts {
        if let Some(stem) = std::path::Path::new(s).file_stem().and_then(|x| x.to_str()) {
            *stems.entry(stem.to_string()).or_default() += 1;
        }
    }
    stems.values().all(|c| *c == 1)
}

Prevention

When it happens

Trigger: `[package] build = ["a/build.rs", "b/build.rs"]` — both files have stem `build`. The `paths.len() > 1` branch at targets.rs:922 flags the stem and bails after building the message.

Common situations: Organizing build scripts in subdirectories that share the conventional `build.rs` name. Merging two crates' build steps into one package. Renaming directories but not the script files.

Related errors


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

Appendix: source

Thrown at src/workspace/parser/targets.rs:928

            .file_stem()
            .and_then(|s| s.to_str())
            .expect("previously normalized");
        seen.entry(stem)
            .or_insert_with(Vec::new)
            .push(script.as_str());
    }
    let mut conflict_file_stem = false;
    let mut err_msg = String::from(
        "found build scripts with duplicate file stems, but all build scripts must have a unique file stem",
    );
    for (stem, paths) in seen {
        if paths.len() > 1 {
            conflict_file_stem = true;
            write!(&mut err_msg, "\n  for stem `{stem}`: {}", paths.join(", "))?;
        }
    }
    if conflict_file_stem {
        anyhow::bail!(err_msg);
    }
    Ok(())
}

fn configure(
    toml: &TomlTarget,
    target: &mut Target,
    target_kind_human: &str,
    warnings: &mut Vec<String>,
) -> CargoResult<()> {
    let t2 = target.clone();
    target
        .set_tested(toml.test.unwrap_or_else(|| t2.tested()))
        .set_doc(toml.doc.unwrap_or_else(|| t2.documented()))
        .set_doctest(toml.doctest.unwrap_or_else(|| t2.doctested()))
        .set_benched(toml.bench.unwrap_or_else(|| t2.benched()))
        .set_harness(toml.harness.unwrap_or_else(|| t2.harness()))
        .set_proc_macro(toml.proc_macro().unwrap_or_else(|| t2.proc_macro()))

View on GitHub (pinned to eb98b54bc9)