embassy-rs/embassy · error

Multiple stm32xx Cargo features enabled

Error message

Multiple stm32xx Cargo features enabled

What it means

embassy-stm32's build script requires exactly one chip target: it collects env vars starting with CARGO_FEATURE_STM32 (excluding CARGO_FEATURE_STM32_HRTIM) and asserts a single one. Multiple chip features cannot be unified into one generated codebase, so it panics.

Solutions

  1. Keep exactly one stm32xx chip feature enabled per build (fix Cargo.toml features array)
  2. Inspect `cargo tree -f '{p} {f}'` to find which crate pulls in the second chip feature and remove it
  3. Split multi-board projects into separate packages or use a Cargo workspace where each package has its own single chip feature

Example fix

# before
features = ["stm32f103c8", "stm32f411ce"]
# after
features = ["stm32f103c8"]
Defensive patterns

Strategy: validation

Validate before calling

# ensure a single chip feature per build
cargo tree -f '{p} {f}' | grep -oE 'stm32[a-z0-9]+' | sort -u | wc -l  # expect 1

Prevention

When it happens

Trigger: Cargo feature unification enables two chip features (e.g. stm32f103c8 + stm32f411ce) at once — usually because two crates in the same build each request a different chip, or a Cargo.toml lists two chip features.

Common situations: Same package used by binaries for different boards in one workspace; listing two chip features 'to be safe'; a dependency enabling a second chip feature.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/224ace08ebfd44ef. Report an issue: GitHub.

Appendix: source

Thrown at embassy-stm32/build.rs:59

fn main() {
    let mut cfgs = common::CfgSet::new();
    common::set_target_cfgs(&mut cfgs);

    if std::env::var("CARGO_FEATURE_RT").is_err()
        && std::env::var("CARGO_CFG_TARGET_OS") == Ok("none".to_string())
        && std::env::var("CARGO_CFG_TARGET_ARCH") == Ok("arm".to_string())
    {
        println!("cargo::warning=Building for bare-metal ARM without `rt` feature: interrupts will loop forever.");
    }

    let chip_name = match env::vars()
        .map(|(a, _)| a)
        .filter(|x| x.starts_with("CARGO_FEATURE_STM32") && x != "CARGO_FEATURE_STM32_HRTIM")
        .get_one()
    {
        Ok(x) => x,
        Err(GetOneError::None) => panic!("No stm32xx Cargo feature enabled"),
        Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"),
    }
    .strip_prefix("CARGO_FEATURE_")
    .unwrap()
    .to_ascii_lowercase();

    eprintln!("chip: {chip_name}");

    for p in METADATA.peripherals {
        if let Some(r) = &p.registers {
            // The AES driver enables the peripheral's clock, which the metadata does not
            // know for the AES of some chips (STM32L0, L1, F423): no driver there.
            if r.kind == "aes" && p.rcc.is_none() {
                continue;
            }
            cfgs.enable(r.kind);
            foreach_version_cfg(&mut cfgs, r.kind, r.version, |cfgs, cfg_name| {
                cfgs.enable(cfg_name);
            });

View on GitHub (pinned to 463a07b963)