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

  1. Create the subscriber exactly once and share it (or pass it) to all consumers
  2. Drop the existing subscriber before creating a new one
  3. 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

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


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)