{"record":{"id":"bfd203d7f4846d78","repo":"embassy-rs/embassy","slug":"bad-mode-bfd203","errorCode":null,"errorMessage":"Bad mode","messagePattern":"Bad mode","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-stm32/src/can/fdcan.rs","lineNumber":975,"sourceCode":"            Err(e) => Err(e),\n        }\n    }\n}\n\nenum TxMode {\n    NonBuffered(AtomicWaker),\n    ClassicBuffered(super::common::ClassicBufferedTxInner),\n    FdBuffered(super::common::FdBufferedTxInner),\n}\n\nimpl TxMode {\n    fn register(&self, arg: &core::task::Waker) {\n        match self {\n            TxMode::NonBuffered(waker) => {\n                waker.register(arg);\n            }\n            _ => {\n                panic!(\"Bad mode\");\n            }\n        }\n    }\n\n    /// Queues the message to be sent but exerts backpressure.  If a lower-priority\n    /// frame is dropped from the mailbox, it is returned.  If no lower-priority frames\n    /// can be replaced, this call asynchronously waits for a frame to be successfully\n    /// transmitted, then tries again.\n    async fn write_generic<F: embedded_can::Frame + CanHeader>(info: &'static Info, frame: &F) -> Option<F> {\n        poll_fn(|cx| {\n            info.state.lock(|s| {\n                s.borrow_mut().tx_mode.register(cx.waker());\n            });\n\n            if let Ok(dropped) = info.regs.write(frame) {\n                return Poll::Ready(dropped);\n            }\n","sourceCodeStart":957,"sourceCodeEnd":993,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-stm32/src/can/fdcan.rs#L957-L993","documentation":"This panic comes from TxMode::register in the FDCAN driver's TX ring. The ring's waker slot is being registered while its TxMode variant is not NonBuffered (e.g. a buffered or classic mode), so the NonBuffered-specific register() path is invoked on an incompatible mode. The library treats this as a programmer error: the channel was configured with a different TX mode than the one whose API is being called.","triggerScenarios":"Calling the non-buffered async TX registration/send path on a TxRing whose TxMode is TxMode::Buffered or another non-non-buffered variant — i.e. the CAN instance is set up in buffered TX mode but non-buffered waker registration is invoked.","commonSituations":"Reusing non-buffered TX code on a peripheral configured with buffered TX; changing the CAN TX mode in the peripheral config without updating async send code that assumes NonBuffered; mixing buffered and non-buffered send APIs on the same channel.","solutions":["Use the buffered-mode send API matching the configured TxMode, or configure the channel for non-buffered TX.","Audit which TxMode the TxRing was constructed with and call only the corresponding mode's methods.","Handle all TxMode variants in the match if both modes must share the registration code."],"exampleFix":"// before\nmatch self {\n    TxMode::NonBuffered(w) => w.register(arg),\n    _ => panic!(\"Bad mode\"),\n}\n// after\nmatch self {\n    TxMode::NonBuffered(w) => w.register(arg),\n    TxMode::Buffered(w) => w.register(arg), // or construct ring with TxMode::NonBuffered\n    _ => panic!(\"Bad mode\"),\n}","handlingStrategy":"type-guard","validationCode":"if let TxMode::NonBuffered(_) = tx_ring.mode() { /* safe to use non-buffered API */ }","typeGuard":"fn is_non_buffered(mode: &TxMode) -> bool { matches!(mode, TxMode::NonBuffered(_)) }","tryCatchPattern":null,"preventionTips":["Keep TX mode configuration and TX API usage in one place so they cannot diverge","Prefer mode-specific wrapper types over panicking default arms","Review CAN peripheral config when migrating async TX code between boards"],"tags":["embedded","can","panic","invalid-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}