embassy-rs/embassy · error
Task is already in use
Error message
Task is already in use
What it means
new_many_to_many panics when a task's SUBSCRIBE register already holds a non-zero value, meaning another PPI/DPPI channel is already subscribed to that task. This guard prevents silently reconfiguring hardware task routing. The driver checks the register with a volatile read before writing the new channel configuration.
Solutions
- Drop or tear down the existing PPI channel that subscribes to the same task before calling new_many_to_many again.
- Audit the task list for duplicates — ensure no task appears in two configurations at once.
- If reconfiguring is intentional, disable the old DPPI channel and zero its SUBSCRIBE registers first.
- Restructure the application so one channel handles all events->task combinations for a shared task.
Example fix
// before let c1 = ManyToMany::new_many_to_many(p.PPI_CH0, [timer_event], [timer_task_clear]); let c2 = ManyToMany::new_many_to_many(p.PPI_CH1, [other_event], [timer_task_clear]); // panics // after let c1 = ManyToMany::new_many_to_many(p.PPI_CH0, [timer_event, other_event], [timer_task_clear]); drop(c1); // if a rebuild is truly needed, drop before recreating
Defensive patterns
Strategy: validation
Validate before calling
// Track claimed tasks in application state; check before configuring.
let task_reg = task.subscribe_reg().read_volatile();
if task_reg != 0 {
// task already subscribed: reuse existing channel or tear it down first
} Prevention
- Keep one owner module for all PPI channel construction so task/event claims are centralized.
- Drop channel wrappers explicitly before reconfiguration.
- Never build two channel structs referencing the same Task peripheral value.
When it happens
Trigger: Calling dppi::ManyToMany::new_many_to_many(ch, events, tasks) with a Task whose SUBSCRIBE register is non-zero — i.e. the task was already claimed by a previously configured PPI channel (via new_one_to_one, new_many_to_many, or manual register writes) that is still enabled or not torn down.
Common situations: Configuring two PPI channels that both drive the same timer task (e.g. two CLEAR/START task connections); calling new_many_to_many twice with an overlapping task; dropping a previous channel struct without disabling the DPPI channel so registers stay non-zero; reconfiguring after a soft reset without clearing SUBSCRIBE registers.
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
- Event is already in use
- Boot prepare error
- unwrap of ` ` failed
- unwrap of ` ` failed
- Bad 802.15.4 channel
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/0930596e6bbb75fa.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-nrf/src/ppi/dppi.rs:34
}
}
impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> {
/// Configure PPI channel to trigger both `task1` and `task2` on `event`.
pub fn new_one_to_two(ch: Peri<'d, C>, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self {
Ppi::new_many_to_many(ch, [event], [task1, task2])
}
}
impl<'d, C: ConfigurableChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize>
Ppi<'d, C, EVENT_COUNT, TASK_COUNT>
{
/// Configure a DPPI channel to trigger all `tasks` when any of the `events` fires.
pub fn new_many_to_many(ch: Peri<'d, C>, events: [Event<'d>; EVENT_COUNT], tasks: [Task<'d>; TASK_COUNT]) -> Self {
let val = DPPI_ENABLE_BIT | (ch.number() as u32 & DPPI_CHANNEL_MASK);
for task in tasks {
if unsafe { task.subscribe_reg().read_volatile() } != 0 {
panic!("Task is already in use");
}
unsafe { task.subscribe_reg().write_volatile(val) }
}
for event in events {
if unsafe { event.publish_reg().read_volatile() } != 0 {
panic!("Event is already in use");
}
unsafe { event.publish_reg().write_volatile(val) }
}
Self { ch, events, tasks }
}
}
impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> {
/// Enables the channel.
pub fn enable(&mut self) {
let n = self.ch.number();View on GitHub (pinned to 463a07b963)