embassy-rs/embassy · critical

PanicRawMutex locked from multiple contexts

Error message

PanicRawMutex locked from multiple contexts

What it means

PanicRawMutex is a raw mutex intended only for use inside panic handlers and other single-context settings; it does not block or spin. Its lock() detects double acquisition via an atomic flag and panics rather than waiting, because blocking inside a panic handler is forbidden.

Solutions

  1. Ensure PanicRawMutex-protected resources are only accessed from one context (the panic handler).
  2. Switch the shared resource to a blocking raw mutex (CriticalSectionRawMutex or ThreadModeRawMutex) if multiple contexts must share it.
  3. Add a re-entrancy guard or restructure code so the same mutex is never acquired twice on one call path.

Example fix

// before
static CONSOLE: Console = Console::new(PanicRawMutex::INIT);
// after (used from tasks + handlers)
static CONSOLE: Console = Console::new(CriticalSectionRawMutex::INIT);
Defensive patterns

Strategy: validation

Validate before calling

// Audit call sites: only the panic handler may touch PanicRawMutex resources.
// Prefer CriticalSectionRawMutex for anything shared across contexts.

Prevention

When it happens

Trigger: Calling lock() on a mutex backed by PanicRawMutex while it is already locked — e.g. the same static shared between a panic handler and normal code, or between two interrupt/NMI contexts, or re-entrant locking in one call path.

Common situations: Using a PanicRawMutex-backed channel/format shared with panic printing (defmt-panic / core::fmt buffering) from regular task code; nesting locks of the same mutex; accessing the shared resource in both an exception handler and mainline code.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at embassy-sync/src/blocking_mutex/raw.rs:90

unsafe impl Send for PanicRawMutex {}
unsafe impl Sync for PanicRawMutex {}

impl PanicRawMutex {
    /// Create a new `PanicRawMutex`.
    pub const fn new() -> Self {
        Self {
            locked: AtomicBool::new(false),
        }
    }
}

unsafe impl RawMutex for PanicRawMutex {
    const INIT: Self = Self::new();

    fn lock<R>(&self, f: impl FnOnce() -> R) -> R {
        #[cfg(target_has_atomic = "8")]
        if self.locked.swap(true, Ordering::Relaxed) {
            panic!("PanicRawMutex locked from multiple contexts")
        }

        #[cfg(not(target_has_atomic = "8"))]
        critical_section::with(|_| {
            if self.locked.load(Ordering::Acquire) {
                panic!("PanicRawMutex locked from multiple contexts")
            }
            self.locked.store(true, Ordering::Release);
        });

        let ret = f();

        self.locked.store(false, Ordering::Relaxed);
        ret
    }
}

// ================

View on GitHub (pinned to 463a07b963)