embassy-rs/embassy · error

InterruptExecutor::start() called multiple times on the…

Error message

InterruptExecutor::start() called multiple times on the same executor.

What it means

Panic raised by the aarch32 InterruptExecutor::start implementation's internal state guard. Each interrupt executor can be started exactly once: start() writes the raw executor state into the static slot and enables the software-generated interrupt. A second call would overwrite partially-initialized state and re-arm the SGI, corrupting the running executor. This fires when application or platform init code calls start() on the same 'static executor instance twice, typically from duplicated board-init paths or a restarted module.

Solutions

  1. Call InterruptExecutor::start() exactly once per executor instance, at a single well-known init point
  2. Guard the start call with an application-level 'initialized' flag or OnceCell so repeated init paths are idempotent
  3. If restart is required, create a new static executor instance instead of re-starting the existing one
  4. Audit board startup code so the executor is not started from both main and a secondary-core or reset path
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at embassy-executor/src/platform/aarch32.rs:269 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at embassy-executor/src/platform/aarch32.rs:269

        /// You must set the interrupt priority (in `GICD_IPRIORITYR`) before calling this method.
        /// You MUST NOT do it after.
        ///
        /// The SGI may be configured as either Group 0 or Group 1 in `GICD_IGROUPR0`. You must
        /// configure the group before calling this method, you MUST NOT change it after.
        ///
        /// The SGI is always sent to the CPU that pends it, so on multi-core chips the executor
        /// must only be woken from the core it was started on.
        ///
        /// # Panics
        ///
        /// Panics if `sgi_id` is greater than 15, or if the executor was already started.
        ///
        /// [`SendSpawner`]: crate::SendSpawner
        pub fn start(&'static self, gicd_base: usize, sgi_id: u8) -> crate::SendSpawner {
            assert!(sgi_id < 16, "SGI interrupt ID must be in the range 0..=15");

            if critical_section::with(|cs| self.started.borrow(cs).replace(true)) {
                panic!("InterruptExecutor::start() called multiple times on the same executor.");
            }

            let inner = unsafe {
                let inner = (*self.inner.get()).as_mut_ptr();
                // Initialize the target first: the executor's pender context points to it.
                (&raw mut (*inner).target).write(SgiTarget::new(gicd_base, sgi_id));
                let target = &raw const (*inner).target;
                (&raw mut (*inner).executor).write(raw::Executor::new(target as *mut ()));
                &*inner
            };

            inner.target.enable();

            inner.executor.spawner().make_send()
        }

        /// Get a SendSpawner for this executor
        ///

View on GitHub (pinned to 463a07b963)