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
- Only use mailbox indices 0..=3 as returned by the driver's split_tx/queue APIs.
- Iterate with `0..NUM_MAILBOXES` rather than hardcoded numbers.
- 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
- FDCAN has at most 4 TX mailboxes; never hardcode larger counts.
- Use mailbox handles/indices returned by the driver's split APIs.
- Derive loop bounds from constants, not magic numbers.
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)