embassy-rs/embassy · error
InterruptExecutor::spawner() called on uninitialized…
Error message
InterruptExecutor::spawner() called on uninitialized executor.
What it means
`InterruptExecutor::spawner` reads the executor's inner state which is only initialized by `start`; if `start` was never called the MaybeUninit is still uninitialized and dereferencing would be UB, so the driver checks the `started` flag and panics instead.
Solutions
- Ensure `InterruptExecutor::start(irq)` is called before any `spawner()` call, ideally as the first step in main/boot
- Obtain the `SendSpawner` once after start and pass it down instead of calling `spawner()` ad hoc
- If start order is nondeterministic, restructure so spawner acquisition happens in the same function/after start via a channel or OnceCell
Example fix
// before let spawner = EXECUTOR.spawner(); // start not yet called EXECUTOR.start(irqs::Irq0); // after EXECUTOR.start(irqs::Irq0); let spawner = EXECUTOR.spawner();
Defensive patterns
Strategy: validation
Validate before calling
fn get_spawner_after_start(irq: impl InterruptNumber) -> embassy_executor::SendSpawner {
EXECUTOR.start(irq); // if start may double-run, guard it first
EXECUTOR.spawner()
}
Prevention
- Enforce start-before-spawner ordering in one boot function
- Cache the SendSpawner from start()/spawner() and pass it to modules
- Avoid fetching the spawner from independent init paths
When it happens
Trigger: Calling `EXECUTOR.spawner()` before `EXECUTOR.start(irq)` anywhere in the program, e.g. spawning tasks in a task setup function that runs before the main init sequence.
Common situations: Reordering init code so a module's `statics`/task spawner executes before executor start; a spawn path in a library that assumes the executor is already running; race between two init paths where spawner is fetched first.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- InterruptExecutor::start() called multiple times on the…
- Can only take the executor once
- timer must be stopped before setting counter
- Failed to configure PLL_SYS
- Failed to configure PLL_USB
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/a16c5e53d87ef05e.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-rp/src/executor.rs:256
}
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
unsafe { NVIC::unmask(irq) }
executor.spawner().make_send()
}
/// Get a SendSpawner for this executor
///
/// This returns a [`SendSpawner`](embassy_executor::SendSpawner) you can use to spawn tasks on this
/// executor.
///
/// This MUST only be called on an executor that has already been started.
/// The function will panic otherwise.
pub fn spawner(&'static self) -> embassy_executor::SendSpawner {
if !critical_section::with(|cs| self.started.borrow(cs).get()) {
panic!("InterruptExecutor::spawner() called on uninitialized executor.");
}
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
executor.spawner().make_send()
}
}
}
View on GitHub (pinned to 463a07b963)