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

  1. Ensure the CAN peripheral's mode/split configuration is fixed for the lifetime of all Receiver handles; drop receivers before reconfiguring
  2. Do not mix buffered and non-buffered receiver usage on the same peripheral; create readers only from the mode you actually configured
  3. 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

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)