{"record":{"id":"41c9f29ad2e5bea1","repo":"embassy-rs/embassy","slug":"event-channel-not-initialized","errorCode":null,"errorMessage":"EVENT_CHANNEL not initialized","messagePattern":"EVENT_CHANNEL not initialized","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"embassy-stm32-wpan/src/wba/linklayer_plat.rs","lineNumber":165,"sourceCode":"\n// Critical-section restore token for IRQ enable/disable pairing.\n// Only written when the IRQ disable counter transitions 0->1, and consumed when it transitions 1->0.\nstatic mut CS_RESTORE_STATE: Option<critical_section::RestoreState> = None;\n\n// Optional hardware RNG instance for true random number generation.\n// The RNG peripheral pointer is stored here to be used by LINKLAYER_PLAT_GetRNG.\n// This must be set by the application using `set_rng_instance` before the link layer requests random numbers.\npub(crate) static mut PLATFORM: Option<&'static Platform> = None;\n\npub(crate) static mut EVENT_CHANNEL: Option<zerocopy_channel::Sender<'static, CriticalSectionRawMutex, ChannelPacket>> =\n    None;\n\nconst fn get_platform() -> &'static Platform {\n    unsafe { PLATFORM.as_ref().expect(\"PLATFORM not initialized\") }\n}\n\nconst fn get_channel() -> &'static mut zerocopy_channel::Sender<'static, CriticalSectionRawMutex, ChannelPacket> {\n    unsafe { EVENT_CHANNEL.as_mut().expect(\"EVENT_CHANNEL not initialized\") }\n}\n\n// ============================================================================\n// AES-128 ECB Hardware Acceleration (Embassy driver)\n// ============================================================================\n\n/// Perform AES-128 ECB encryption using the Embassy AES driver.\nfn aes_ecb_encrypt(key: &[u8; 16], input: &[u8; 16], output: &mut [u8; 16]) {\n    get_platform().borrow_aes(|aes| {\n        let cipher = AesEcb::new(key);\n        let mut ctx = aes.start(&cipher, Direction::Encrypt);\n        aes.payload_blocking(&mut ctx, input, output, true).unwrap();\n        aes.finish_blocking(ctx).unwrap();\n    });\n}\n\n// ============================================================================\n// AES-CMAC (RFC 4493) Implementation","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-stm32-wpan/src/wba/linklayer_plat.rs#L147-L183","documentation":"The WPAN (BLE) stack on STM32WBA stores the outgoing event channel sender in a static OptionalCell (EVENT_CHANNEL), populated during stack initialization. get_channel() unwraps it with expect(); if the BLE link layer callback fires before the embassy-stm32-wpan stack has been initialized (the static is still None), this panic fires. It is an internal invariant: the hardware CPU2 firmware raised a BLE event but the Rust-side sink was never set up.","triggerScenarios":"Calling BLECB_Indication (i.e., CPU2 delivering a BLE event over IPCC) before the application has constructed and run the WPAN stack (e.g. before Ble::new / stack init registers the zerocopy channel sender). Also occurs if stack init was skipped or ran on a different execution path than the interrupt that services IPCC channel 1.","commonSituations":"Enabling the BLE IRQ or starting CPU2 wireless firmware too early in main; forgetting to call the WPAN stack initialization before enabling interrupts; copying example code that enables the radio IRQ but drops the stack builder; running the BLE interrupt handler in tests/benchmarks without a full stack.","solutions":["Initialize the WPAN stack (create the BLE stack so EVENT_CHANNEL is populated) before enabling the IPCC/BLE interrupts or before CPU2 can send events.","Ensure CPU2 wireless firmware is started only after the Rust-side stack initialization completes.","Do not manually enable BLECB/IPCC channel interrupts; let the embassy-wpan stack set them up.","If the panic appears after a version upgrade, update embassy-stm32-wpan and its docs-following init sequence, since the init API may have changed."],"exampleFix":"// before\nunsafe { CPU2.start_firmware() };\nT::Interrupt::enable(); // IRQ may fire before stack init\nlet mut ble = Ble::new(...);\n\n// after\nlet mut ble = Ble::new(...); // initializes EVENT_CHANNEL\nunsafe { CPU2.start_firmware() };\nT::Interrupt::enable();","handlingStrategy":"validation","validationCode":"// Before enabling BLE IRQs / starting CPU2, assert stack init happened:\n// initialize the WPAN stack first; the embassy API guarantees EVENT_CHANNEL is set after Ble/Stack::new.\nlet mut ble = Ble::new(p.IPCC, p.RADIO, Irqs);\nlet mut stack = embassy_stm32_wpan::ble::Stack::new(&mut ble);\n// only now:\n// stack.run(...) / enable interrupts / start CPU2","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run the WPAN stack initialization before starting CPU2 wireless firmware or enabling IPCC/BLE interrupts.","Never enable BLE-related IRQs manually; let the driver do it.","Keep the stack object alive for the whole program; do not drop it after spawn."],"tags":["embedded","stm32","ble","initialization-order","panic"],"backgroundTag":"internal-invariant-violation","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"}