embassy-rs/embassy · error

Multiple time-driver-xxx Cargo features enabled

Error message

Multiple time-driver-xxx Cargo features enabled

What it means

embassy-stm32 selects its time-driver timer via Cargo features 'time-driver-timX'/'time-driver-any'. The build script reads CARGO_FEATURE_TIME_DRIVER_* env vars and panics if more than one is set, because only one timer driver can be compiled in.

Solutions

  1. Keep exactly one time-driver-* feature enabled (use 'time-driver-any' if you have no specific timer requirement)
  2. Remove the extra time-driver features from Cargo.toml
  3. Check `cargo tree -f '{p} {f}'` to find which crate unifies in the second time-driver feature

Example fix

# before
features = ["stm32f103c8", "time-driver-tim2", "time-driver-tim3"]
# after
features = ["stm32f103c8", "time-driver-any"]
Defensive patterns

Strategy: validation

Validate before calling

# exactly one time-driver feature
features=$(cargo tree -f '{f}' -p embassy-stm32); echo "$features" | grep -oE 'time-driver-[a-z0-9]+' | sort -u | wc -l  # expect 1

Prevention

When it happens

Trigger: Enabling two or more time-driver features simultaneously (e.g. time-driver-tim2 + time-driver-tim3, or one of them plus time-driver-any).

Common situations: Cargo feature unification from another crate in the workspace enabling a different time driver; manually adding a second time-driver feature 'to be safe'; merging Cargo.toml changes from two branches.

Related errors


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

Appendix: source

Thrown at embassy-stm32/build.rs:357

            )
        }
    }

    // ========
    // Handle time-driver-XXXX features.

    let time_driver = match env::vars()
        .map(|(a, _)| a)
        .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
        .get_one()
    {
        Ok(x) => Some(
            x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
                .unwrap()
                .to_ascii_lowercase(),
        ),
        Err(GetOneError::None) => None,
        Err(GetOneError::Multiple) => panic!("Multiple time-driver-xxx Cargo features enabled"),
    };

    let time_driver_singleton: String = match time_driver.as_deref() {
        None => String::new(),
        Some("any") => {
            // The driver uses CC1 for the halfway-point interrupt and CC2 for the alarm,
            // so basic and 1-channel timers are out. Rank the rest:
            // 1. 32-bit timers first: the counter overflows far less often, so the driver
            //    takes far fewer interrupts. Then less-featured first, to leave the more
            //    capable timers to the user.
            // 2. Within a category, larger TIM number first.
            METADATA
                .peripherals
                .iter()
                .filter(|p| singletons.contains(&p.name.to_string()))
                .filter_map(|p| {
                    let regs = p.registers.as_ref()?;
                    if regs.kind != "timer" {

View on GitHub (pinned to 463a07b963)