embassy-rs/embassy · error

Event is already in use

Error message

Event is already in use

What it means

new_many_to_many panics when an event's PUBLISH register already holds a non-zero value, meaning another PPI/DPPI channel already publishes that event. This prevents silently rewiring hardware event routing. The driver volatile-reads the PUBLISH register before writing the new channel configuration.

Solutions

  1. Drop or disable the existing PPI channel that publishes the same event before creating the new one.
  2. Check for duplicate events in the events array and across existing channel configurations.
  3. If one event must trigger multiple tasks, list multiple tasks on a single channel instead of creating parallel channels for the same event.
  4. After a soft reset, explicitly clear PUBLISH registers before reconfiguration.

Example fix

// before
let c1 = ManyToMany::new_many_to_many(p.PPI_CH0, [event], [task_a]);
let c2 = ManyToMany::new_many_to_many(p.PPI_CH1, [event], [task_b]); // panics
// after
let c1 = ManyToMany::new_many_to_many(p.PPI_CH0, [event], [task_a, task_b]);
Defensive patterns

Strategy: validation

Validate before calling

// Check the event's PUBLISH register before configuring a channel.
let pub_reg = event.publish_reg().read_volatile();
if pub_reg != 0 {
    // event already published by another channel
}

Prevention

When it happens

Trigger: Calling dppi::ManyToMany::new_many_to_many(ch, events, tasks) with an Event whose PUBLISH register is non-zero — e.g. the event was already published by a previously created channel (new_one_to_one/new_many_to_many) or written manually, and that channel has not been disabled or dropped.

Common situations: Two radio peripherals (e.g. TIMER compare and radio READY) feeding two different channels; routing the same GPIOTE event to multiple sink tasks through separate channels; re-running initialization code after a state reset without clearing PUBLISH 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


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

Appendix: source

Thrown at embassy-nrf/src/ppi/dppi.rs:40

        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();
        self.ch.regs().chenset().write(|w| w.0 = 1 << n);
    }

    /// Disables the channel.
    pub fn disable(&mut self) {
        let n = self.ch.number();

View on GitHub (pinned to 463a07b963)