embassy-rs/embassy · error
the RNG is in use
Error message
the RNG is in use
What it means
The embassy RNG global driver is guarded by a `Mutex` over a single `Option<Rng<'static, Blocking>>`. `with_rng` uses `try_lock` and panics with "the RNG is in use" if the lock is already held — meaning two tasks (or an interrupt context) are concurrently using the RNG. Embassy mutexes are not reentrant, so nested/parallel access is a hard error.
Solutions
- Serialize RNG access yourself: perform random reads from a single task, or use a channel/actor that owns randomness
- Retry or defer one consumer — since it's `try_lock`, moving the second call to another tick avoids contention
- Check for accidental nesting: don't call another RNG-based API while holding a random-generating call (e.g. generating a UUID inside `fill_bytes` handling)
- Use an embassy `signal`/`mutex` wrapper so callers await instead of panicking
Example fix
// before let a = rand_bytes().await; // task A let b = rand_bytes().await; // task B, same tick -> try_lock panic // after static RNG_SEM: Semaphore<1> = Semaphore::new(1); let permit = RNG_SEM.acquire().await; let a = rand_bytes().await; drop(permit); // then task B proceeds safely
Defensive patterns
Strategy: validation
Validate before calling
// Ensure only one task generates randomness at a time static RNG_GATE: Mutex<CriticalSectionRawMutex, ()> = Mutex::new(()); let _g = RNG_GATE.lock().await; // serialize callers instead of try_lock panicking
Try / catch
// Embassy panics aren't catchable; avoid contention instead:
match DRIVER.try_lock() {
Ok(_) => { /* safe to generate randomness */ }
Err(_) => { /* defer to next tick or return Pending */ }
} Prevention
- Funnel all randomness through one dedicated task/actor
- Never nest RNG-based calls inside another RNG operation
- Audit that no interrupt context calls RNG-backed APIs
- Add an owned semaphore around random generation in application code
When it happens
Trigger: Two async tasks concurrently calling RNG-backed APIs (e.g. `fill_bytes` via `Rng`/random helpers); calling an RNG function from inside another RNG callback (re-entrant nesting) so the mutex is locked twice in the same execution context.
Common situations: Multiple subsystems (TLS, device ID generation, UUID generation) sampling randomness simultaneously; spawning two tasks that both draw entropy at startup; blocking RNG call inside an async context that also uses the RNG from an executor thread.
Related errors
- PanicRawMutex locked from multiple contexts
- the AES is in use
- Bad Mode
- Bad mode
- RNG error persists after reset; check the RCC clock…
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/3c44a810390d5975.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/rng.rs:678
pub(crate) mod driver {
use embassy_crypto::Error as CryptoError;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::mutex::Mutex;
use super::{MAX_RESET_RETRIES, Rng};
use crate::mode::Blocking;
foreach_peripheral!(
(rng, $inst:ident) => {
type Instance = crate::peripherals::$inst;
};
);
static DRIVER: Mutex<CriticalSectionRawMutex, Option<Rng<'static, Blocking>>> = Mutex::new(None);
/// Runs `f` on the RNG, starting it first if needed.
pub(crate) fn with_rng<R>(f: impl FnOnce(&mut Rng<'static, Blocking>) -> R) -> R {
let mut driver = DRIVER.try_lock().expect("the RNG is in use");
let rng = driver.get_or_insert_with(|| {
let peri = unsafe { Instance::steal() };
#[cfg(rng_v1)]
{
Rng::new_inner(peri)
}
#[cfg(not(rng_v1))]
{
Rng::new_inner(peri, super::RngConfig::default())
}
});
f(rng)
}
/// Starts the RNG if it is not running yet.
pub(crate) fn ensure_running() {
with_rng(|_| ());
}View on GitHub (pinned to 463a07b963)