embassy-rs/embassy · error
Blocking at SleepLevel
Error message
Blocking at SleepLevel {:?} would overflow What it means
Each SleepLevel has a saturating u8 counter (SLEEP_BLOCKS) of active WakeGuards; block() detects that another increment would hit u8::MAX and overflow the count, which would corrupt the sleep-blocking bookkeeping. This is a driver-internal guard against ~255 outstanding WakeGuards at one level, effectively indicating a WakeGuard leak.
Solutions
- Drop WakeGuards when done rather than keeping them alive; each guard increments the counter and its Drop decrements it.
- Audit code that std::mem::forget's or leaks a WakeGuard.
- If >255 simultaneously-live guards at one sleep level is legitimate, raise the counter type from u8.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at embassy-mspm0/src/low_power/mod.rs:85 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of embassy-rs/embassy@463a07b963 (2026-09-10).
Data as JSON: /api/errors/c94a9519de84564c.
Report an issue: GitHub.
Appendix: source
Thrown at embassy-mspm0/src/low_power/mod.rs:85
mspm0g151x,
mspm0g351x,
mspm0g518x,
mspm0c110x,
mspm0c1105_c1106,
mspm0h321x
)))]
compile_error!("the `low-power` feature is not implemented for this chip family");
pub use crate::sysctl::SleepLevel;
static SLEEP_BLOCKS: [AtomicU8; 5] = [const { AtomicU8::new(0) }; 5];
/// Block sleep at `level` and every deeper mode. Paired with [`unblock`] by
/// [`WakeGuard`](crate::sysctl::WakeGuard).
pub(crate) fn block(level: SleepLevel) {
trace!("Blocking sleep at level {:?}", level);
if SLEEP_BLOCKS[level as usize].fetch_add(1, Ordering::Relaxed) == (u8::MAX - 1) {
panic!("Blocking at SleepLevel {:?} would overflow", level)
};
}
/// Remove a block previously added at `level`.
pub(crate) fn unblock(level: SleepLevel) {
trace!("Unblocking sleep at level {:?}", level);
SLEEP_BLOCKS[level as usize].fetch_sub(1, Ordering::Relaxed);
}
/// Deepest mode currently permitted, or `None` if all deep sleep is blocked.
fn deepest_allowed() -> Option<SleepLevel> {
for (i, blocks) in SLEEP_BLOCKS.iter().enumerate() {
if blocks.load(Ordering::Relaxed) > 0 {
return i.checked_sub(1).map(|j| SleepLevel::LEVELS[j]);
}
}
Some(SleepLevel::Standby1)
}View on GitHub (pinned to 463a07b963)