embassy-rs/embassy · error

Bad mailbox

Error message

Bad mailbox

What it means

`FdcanTxFrame::flush` panics with "Bad mailbox" when the transmit mailbox index is greater than 3. The FDCAN peripheral provides at most 4 TX buffers used as mailboxes, so any index beyond that is an invalid argument. Because it is a panic, it aborts the task rather than returning an error.

Solutions

  1. Only use mailbox indices 0..=3 as returned by the driver's split_tx/queue APIs.
  2. Iterate with `0..NUM_MAILBOXES` rather than hardcoded numbers.
  3. Track mailbox handles returned by the driver instead of synthesizing indices.

Example fix

// before
for idx in 0..8 {
    tx.flush(idx).await;
}
// after
for idx in 0..4 {
    tx.flush(idx).await;
}
Defensive patterns

Strategy: validation

Validate before calling

fn valid_mailbox(idx: usize) -> bool { idx < 4 }

Prevention

When it happens

Trigger: Calling `flush(idx)` (or APIs that forward to it, like awaiting a transmitted frame handle) with a mailbox index > 3 — e.g. iterating mailboxes with the wrong limit or reusing an index from a differently-configured buffer count.

Common situations: Hardcoding mailbox counts instead of using the driver's constants; code written for other CAN peripherals with more mailboxes; off-by-one loops over `0..=3` becoming `0..4` on inclusive ranges.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10). Data as JSON: /api/errors/7e26a9bfd40c4bc8. Report an issue: GitHub.

Appendix: source

Thrown at embassy-stm32/src/can/fdcan.rs:329

    properties: Properties,
    info: InfoRef,
}

impl<'d> Can<'d> {
    /// Get driver properties
    pub fn properties(&self) -> &Properties {
        &self.properties
    }

    /// Flush one of the TX mailboxes.
    pub async fn flush(&self, idx: usize) {
        poll_fn(|cx| {
            self.info.state.lock(|s| {
                s.borrow_mut().tx_mode.register(cx.waker());
            });

            if idx > 3 {
                panic!("Bad mailbox");
            }
            let idx = 1 << idx;
            if !self.info.regs.regs.txbrp().read().trp(idx) {
                return Poll::Ready(());
            }

            Poll::Pending
        })
        .await;
    }

    /// Queues the message to be sent but exerts backpressure.  If a lower-priority
    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames
    /// can be replaced, this call asynchronously waits for a frame to be successfully
    /// transmitted, then tries again.
    pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
        TxMode::write(&self.info, frame).await
    }

View on GitHub (pinned to 463a07b963)