embassy-rs/embassy · error
ZeroCopyPubSub cannot have multiple subscribers
Error message
ZeroCopyPubSub cannot have multiple subscribers
What it means
ZeroCopyPubSub in embassy-stm32-wpan's net layer is implemented as a single-slot publisher/subscriber: it stores one Option<Signal> behind a mutex and replaces it on subscribe. If a signal is already stored, a second Subscriber::new call panics because the design supports at most one subscriber.
Solutions
- Create the subscriber exactly once and share it (or pass it) to all consumers
- Drop the existing subscriber before creating a new one
- Restructure to use a real multi-subscriber pub-sub crate (e.g. embassy-sync pubsub channel) if multiple consumers are required
Example fix
// before let sub1 = pubsub.subscriber(); let sub2 = pubsub.subscriber(); // panics // after let sub1 = pubsub.subscriber(); // pass sub1 to every task that needs it, or drop(sub1) before re-subscribing
Defensive patterns
Strategy: validation
Validate before calling
// create the subscriber once, e.g. in a static/OnceCell
static SUB: OnceCell<Subscriber> = OnceCell::new();
if SUB.get().is_none() { let _ = SUB.set(pubsub.subscriber()); } Prevention
- Construct subscribers in a single init location
- Never re-create a subscriber on retry without dropping the old one
- Use embassy-sync pubsub channel if you need multiple subscribers
When it happens
Trigger: Calling Subscriber::new (via the pub-sub API, e.g. obtaining a subscriber from ZeroCopyPubSub) more than once for the same channel while the previous subscriber still exists.
Common situations: Constructing two subscribers in different parts of the app (e.g. one for BLE, one for logging); spawning a second task that subscribes after an earlier task already did; retry logic that re-creates a subscriber without dropping the old one.
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
- Boot prepare error
- WatchdogChannels::All is not supported for AWD2; use…
- WatchdogChannels::All is not supported for AWD2; use…
- WatchdogChannels::All is not supported for AWD3; use…
- WatchdogChannels::All is not supported for AWD3; use…
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/4b027027da6bc79e.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32-wpan/src/net/util.rs:42
pub fn publish(&self, event: B) {
if let Some(signal) = self.event.borrow().borrow_mut().as_ref() {
signal.signal(event);
}
}
pub fn subscribe<'a>(&'a self) -> Subscriber<'a, B> {
Subscriber::new(&self.event)
}
}
pub struct Subscriber<'a, B: ControllerToHostPacketBox> {
event: &'a blocking_mutex::Mutex<NoopRawMutex, RefCell<Option<Signal<NoopRawMutex, B>>>>,
}
impl<'a, B: ControllerToHostPacketBox> Subscriber<'a, B> {
fn new(event: &'a blocking_mutex::Mutex<NoopRawMutex, RefCell<Option<Signal<NoopRawMutex, B>>>>) -> Self {
if event.borrow().borrow_mut().replace(Signal::new()).is_some() {
panic!("ZeroCopyPubSub cannot have multiple subscribers ")
}
Self { event }
}
pub async fn wait(&self) -> B {
poll_fn(|cx| self.event.borrow().borrow_mut().as_ref().unwrap().wait().poll_unpin(cx)).await
}
}
impl<'a, B: ControllerToHostPacketBox> Drop for Subscriber<'a, B> {
fn drop(&mut self) {
self.event.borrow().borrow_mut().take();
}
}
View on GitHub (pinned to 463a07b963)