embassy-rs/embassy · error
Bad Mode
Error message
Bad Mode
What it means
This panic fires inside the bxCAN receive path when the peripheral's RX mode is neither Buffered nor NonBuffered-with-queue at the point try_read is called — i.e. the RxFifo/TxMode state machine in embassy-stm32's can driver is in an unexpected variant. It indicates internal invariant violation: the receive mode was changed (or torn down) while a read was in flight, which the safe API contract forbids.
Solutions
- Ensure the CAN peripheral's mode/split configuration is fixed for the lifetime of all Receiver handles; drop receivers before reconfiguring
- Do not mix buffered and non-buffered receiver usage on the same peripheral; create readers only from the mode you actually configured
- Update embassy-stm32 to the latest version, as the bxcan mode state machine has seen refactors; if it persists with valid single-mode usage, file a bug — it is a driver invariant violation, not user error
Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: before reading, ensure the CAN peripheral was split/configured exactly once and no reconfigure code path runs while this receiver exists (use a type-state/peripheral-ownership design).
Type guard
// Rust: fn is_nonbuffered(rx: &RxMode) -> bool { matches!(rx, RxMode::NonBuffered(_)) } // not directly accessible; enforce by never re-splitting an owned peripheral Try / catch
// Rust: panics cannot be caught in no_std embedded code; prevent by design — keep mode configuration immutable for the life of all handles. Use catch_unwind only as a last resort on hosted targets.
Prevention
- Treat CAN mode configuration as immutable after init
- Drop receivers before any reconfiguration
- Never mix buffered and non-buffered receive APIs
- Keep one owner (single task) for peripheral reconfiguration
When it happens
Trigger: Calling try_read (or the blocking read wrapper) on a can::RxFifo/Rx instance while the CAN state's rx_mode is neither Buffered mode nor the mode that try_read expects — typically after reconfiguring splits/modes while receivers are still alive.
Common situations: Re-splitting or reconfiguring the CAN peripheral at runtime while an old receiver handle is still used; type-confusion from holding a receiver across a mode change; driver misuse in async tasks that outlive the configuration that created them.
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/37bd4249ff41b1d3.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/can/bxcan/mod.rs:904
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
self.info.state.lock(|s| match &s.borrow().rx_mode {
RxMode::Buffered(_) => {
if let Ok(result) = self.rx_buf.try_receive() {
match result {
Ok(envelope) => Ok(envelope),
Err(e) => Err(TryReadError::BusError(e)),
}
} else {
if let Some(err) = self.info.regs.curr_error() {
return Err(TryReadError::BusError(err));
} else {
Err(TryReadError::Empty)
}
}
}
_ => {
panic!("Bad Mode")
}
})
}
/// Waits while receive queue is empty.
pub async fn wait_not_empty(&mut self) {
poll_fn(|cx| self.rx_buf.poll_ready_to_receive(cx)).await
}
/// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
pub fn reader(&self) -> BufferedCanReceiver {
BufferedCanReceiver {
rx_buf: self.rx_buf.receiver().into(),
info: RxInfoRef::new(&self.info),
}
}
/// Accesses the filter banks owned by this CAN peripheral.View on GitHub (pinned to 463a07b963)