embassy-rs/embassy · error
the AES is in use
Error message
the AES is in use
What it means
The embassy-stm32 AES driver wraps the hardware peripheral in a global async Mutex (DRIVER). lock() uses try_lock().expect(...), so it panics instead of waiting whenever the AES peripheral is already held by another concurrent user. The guard is meant to be held only for the duration of a crypto operation; overlapping operations are a programming error.
Solutions
- Serialize AES usage: perform operations from one task, or guard calls with your own shared mutex/semaphore and await properly.
- Use the async (non-blocking) AES API so callers await the lock instead of try_lock-panicking, if available.
- Move blocking AES calls out of interrupt handlers.
- Keep crypto operations short and drop guards promptly (avoid holding the returned MutexGuard across await points of unrelated work).
Example fix
// before let a = aes.encrypt(&k, p1).await; let b = other_task_aes.encrypt(&k, p2).await; // panics if concurrent // after static AES_SEM: Mutex<CriticalSectionRawMutex, AesDriver> = Mutex::new(...); let guard = AES_SEM.lock().await; let a = guard.encrypt(&k, p1).await; let b = guard.encrypt(&k, p2).await;
Defensive patterns
Strategy: try-catch
Validate before calling
// Prevent overlap before calling AES APIs:
// track ownership at the app level
static AES_BUSY: AtomicBool = AtomicBool::new(false);
fn aes_available() -> bool { !AES_BUSY.load(Ordering::Acquire) } Type guard
fn can_encrypt() -> bool {
// only call encrypt/decrypt when no other context holds the AES guard
AES_BUSY.load(core::sync::atomic::Ordering::Acquire) == false
} Try / catch
// Rust panics cannot be caught in embedded; prevent instead.
// Wrap all AES access in one async mutex:
static AES: Mutex<CriticalSectionRawMutex, Aes<'static>> = Mutex::new(Aes::new());
async fn do_aes(f: impl FnOnce(&Aes<'static>) -> ...) -> ... {
let a = AES.lock().await;
f(&a)
} Prevention
- Route all AES calls through a single async Mutex or a dedicated crypto task.
- Never call blocking AES functions from interrupt handlers.
- Hold the driver lock only for the duration of the crypto operation.
When it happens
Trigger: Calling any of encrypt_blocks/decrypt_blocks/encrypt/decrypt/apply_keystream while another task or context already holds the AES lock (e.g. two tasks doing AES-CCM and AES-GCM concurrently, or a blocking call from an interrupt while a task holds the guard).
Common situations: Spawning two tasks that both perform AES operations without synchronization; calling AES from an ISR while the main task is mid-encryption; shared crypto helpers invoked from multiple connection handlers.
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/73310a4b355f7715.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-stm32/src/aes/driver.rs:53
};
);
#[cfg(feature = "embassy-crypto-saes")]
foreach_peripheral!(
(saes, $inst:ident) => {
type BlockingAes = crate::saes::Saes<'static, crate::peripherals::$inst, Blocking>;
static DRIVER: Mutex<CriticalSectionRawMutex, ResumablePeripheral<BlockingAes>> =
Mutex::new(ResumablePeripheral::new_suspended(unsafe { crate::peripherals::$inst::steal() }));
};
);
/// Takes the peripheral, which is clocked for as long as the guard's borrow lives.
fn lock() -> MutexGuard<'static, CriticalSectionRawMutex, ResumablePeripheral<BlockingAes>> {
// The SAES fetches random numbers from the RNG whenever it is reset.
#[cfg(all(feature = "embassy-crypto-saes", feature = "embassy-crypto-rng"))]
crate::rng::driver::ensure_running();
DRIVER.try_lock().expect("the AES is in use")
}
fn map_error(error: super::Error) -> CryptoError {
match error {
super::Error::KeyError => CryptoError::InvalidKey,
super::Error::ConfigError => CryptoError::InvalidInput,
super::Error::ReadError | super::Error::WriteError => CryptoError::HardwareError,
}
}
fn run_in_place<'c, C>(
aes: &mut BlockingAes,
cipher: &'c C,
direction: Direction,
buffer: &mut [u8],
) -> Result<(), CryptoError>
where
C: super::Cipher<'c> + super::CipherSized + super::IVSized,View on GitHub (pinned to 463a07b963)