{"record":{"id":"6bec276ef10756ae","repo":"embassy-rs/embassy","slug":"interruptexecutor-start-called-multiple-times-o-6bec27","errorCode":null,"errorMessage":"InterruptExecutor::start() called multiple times on the same executor.","messagePattern":"InterruptExecutor::start\\(\\) called multiple times on the same executor\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-rp/src/executor.rs","lineNumber":230,"sourceCode":"        /// different \"thread\" (the interrupt), so spawning tasks on it is effectively\n        /// sending them.\n        ///\n        /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from\n        /// a task running in it.\n        ///\n        /// # Interrupt requirements\n        ///\n        /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt).\n        ///\n        /// This method already enables (unmasks) the interrupt, you must NOT do it yourself.\n        ///\n        /// You must set the interrupt priority before calling this method. You MUST NOT\n        /// do it after.\n        ///\n        /// [`SendSpawner`]: embassy_executor::SendSpawner\n        pub fn start(&'static self, irq: impl InterruptNumber) -> embassy_executor::SendSpawner {\n            if critical_section::with(|cs| self.started.borrow(cs).replace(true)) {\n                panic!(\"InterruptExecutor::start() called multiple times on the same executor.\");\n            }\n\n            unsafe {\n                let context = (irq.number() as usize | (current_core() as usize) << 16) as *mut ();\n                (&mut *self.executor.get())\n                    .as_mut_ptr()\n                    .write(raw::Executor::new(context))\n            }\n\n            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };\n\n            unsafe { NVIC::unmask(irq) }\n\n            executor.spawner().make_send()\n        }\n\n        /// Get a SendSpawner for this executor\n        ///","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-rp/src/executor.rs#L212-L248","documentation":"`InterruptExecutor::start` uses a `started` Cell guarded by a critical section to guarantee single initialization; calling `start` a second time on the same static executor instance panics because the executor's inner state is already initialized and re-init would corrupt it.","triggerScenarios":"Calling `executor.start(irq)` twice on the same `static EXECUTOR: InterruptExecutor`, e.g. in both an init function and a later restart path; running the same init code on both cores against a shared static executor; re-running init after a soft reset without re-creating the executor.","commonSituations":"Refactored boot code that invokes the executor setup from two places; a multi-core app where each core calls the same start function; tests running setup twice in one process with a `#[static_executor]`-style static.","solutions":["Call `start` exactly once per executor instance, e.g. in a dedicated `init()` guarded by `OnceLock`/`once` guard","Use distinct static executors if you genuinely need to start executors at different times or priorities","Guard the start call: check `started` first via `spawner()` succeeding, or wrap start in a `critical_section` check","For multi-core, give each core its own InterruptExecutor static"],"exampleFix":"// before\nfn init() { EXECUTOR.start(irqs::Irq0); }\ninit(); init(); // second call panics\n// after\nuse core::sync::atomic::{AtomicBool, Ordering};\nstatic INITED: AtomicBool = AtomicBool::new(false);\nfn init() {\n    if INITED.swap(true, Ordering::SeqCst) { return; }\n    EXECUTOR.start(irqs::Irq0);\n}\n","handlingStrategy":"validation","validationCode":"use core::sync::atomic::{AtomicBool, Ordering};\nstatic EXEC_STARTED: AtomicBool = AtomicBool::new(false);\nfn start_executor_once(irq: impl InterruptNumber) -> Option<embassy_executor::SendSpawner> {\n    if EXEC_STARTED.swap(true, Ordering::SeqCst) { return None; }\n    Some(EXECUTOR.start(irq))\n}\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Call start exactly once from a single init function","Use an atomic/OnceCell guard around start in reusable init code","Give each core its own executor static in multi-core apps"],"tags":["rust","embedded","rp2040","executor","double-init"],"backgroundTag":"invalid-state-transition","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}