embassy-rs/embassy · critical
EVENT_CHANNEL not initialized
Error message
EVENT_CHANNEL not initialized
What it means
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.
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.
Example fix
// before
unsafe { CPU2.start_firmware() };
T::Interrupt::enable(); // IRQ may fire before stack init
let mut ble = Ble::new(...);
// after
let mut ble = Ble::new(...); // initializes EVENT_CHANNEL
unsafe { CPU2.start_firmware() };
T::Interrupt::enable(); Defensive patterns
Strategy: validation
Validate before calling
// Before enabling BLE IRQs / starting CPU2, assert stack init happened: // initialize the WPAN stack first; the embassy API guarantees EVENT_CHANNEL is set after Ble/Stack::new. let mut ble = Ble::new(p.IPCC, p.RADIO, Irqs); let mut stack = embassy_stm32_wpan::ble::Stack::new(&mut ble); // only now: // stack.run(...) / enable interrupts / start CPU2
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Boot prepare error
- ZeroCopyPubSub cannot have multiple subscribers
- WatchdogChannels::All is not supported for AWD2; use…
- WatchdogChannels::All is not supported for AWD2; use…
- WatchdogChannels::All is not supported for AWD3; use…
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/41c9f29ad2e5bea1.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32-wpan/src/wba/linklayer_plat.rs:165
// Critical-section restore token for IRQ enable/disable pairing.
// Only written when the IRQ disable counter transitions 0->1, and consumed when it transitions 1->0.
static mut CS_RESTORE_STATE: Option<critical_section::RestoreState> = None;
// Optional hardware RNG instance for true random number generation.
// The RNG peripheral pointer is stored here to be used by LINKLAYER_PLAT_GetRNG.
// This must be set by the application using `set_rng_instance` before the link layer requests random numbers.
pub(crate) static mut PLATFORM: Option<&'static Platform> = None;
pub(crate) static mut EVENT_CHANNEL: Option<zerocopy_channel::Sender<'static, CriticalSectionRawMutex, ChannelPacket>> =
None;
const fn get_platform() -> &'static Platform {
unsafe { PLATFORM.as_ref().expect("PLATFORM not initialized") }
}
const fn get_channel() -> &'static mut zerocopy_channel::Sender<'static, CriticalSectionRawMutex, ChannelPacket> {
unsafe { EVENT_CHANNEL.as_mut().expect("EVENT_CHANNEL not initialized") }
}
// ============================================================================
// AES-128 ECB Hardware Acceleration (Embassy driver)
// ============================================================================
/// Perform AES-128 ECB encryption using the Embassy AES driver.
fn aes_ecb_encrypt(key: &[u8; 16], input: &[u8; 16], output: &mut [u8; 16]) {
get_platform().borrow_aes(|aes| {
let cipher = AesEcb::new(key);
let mut ctx = aes.start(&cipher, Direction::Encrypt);
aes.payload_blocking(&mut ctx, input, output, true).unwrap();
aes.finish_blocking(ctx).unwrap();
});
}
// ============================================================================
// AES-CMAC (RFC 4493) ImplementationView on GitHub (pinned to 463a07b963)