embassy-rs/embassy · error

unwrap of ` ` failed

Error message

unwrap of `{}` failed: {}: {:?}

What it means

This panic comes from the embassy-nxp `unwrap!` macro, which wraps a `Result`- or `Option`-returning expression and panics when it holds an error variant. The library throws it instead of `.unwrap()` so the panic message includes the caller-supplied context message plus the debug-formatted error value. Any call site that uses `unwrap!(expr, "context")` on a failing expression triggers it.

Solutions

  1. Read the `: {:?}` debug output at the end of the panic to identify the underlying error variant
  2. Find the call site via the stringified expression in the panic message and handle the Result with `?` or a match instead of unwrapping
  3. Fix the root cause the inner error reports (wrong peripheral config, busy hardware, missing init) before retrying
  4. If the expression is an Option that should never be None, add an explicit assert with a domain-specific message

Example fix

// before
let status = unwrap!(i2c.blocking_write_read(addr, &reg, &mut buf), "read failed");
// after
let status = match i2c.blocking_write_read(addr, &reg, &mut buf) {
    Ok(s) => s,
    Err(e) => { defmt::error!("read failed: {:?}", e); return Err(e); }
};
Defensive patterns

Strategy: validation

Validate before calling

fn unwrap_ok<T, E: core::fmt::Debug>(r: Result<T, E>) -> T { match r { Ok(t) => t, Err(e) => { defmt::error!("unwrap failed: {:?}", e); unreachable!() } } } // prefer handling Err explicitly before calling unwrap!

Type guard

fn is_ok<T, E>(r: &Result<T, E>) -> bool { r.is_ok() }

Try / catch

// Rust panics are not catchable per-call; use panic = "abort" awareness or catch_unwind on std targets
let v = std::panic::catch_unwind(|| unwrap!(fallible(), "ctx"));

Prevention

When it happens

Trigger: Calling `unwrap!(some_result, "message")` in embassy-nxp code (or user code using the exported macro) where the expression resolves to `Err(e)` via `fmt::Try::into_result`, or `None` for Options.

Common situations: Peripheral drivers encountering unexpected hardware states (failed init, NACKed I2C transactions, timeout results) that were force-unwrapped; drivers written for a Result-returning API being ported to a version where the call now returns an error type.

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/577da8b76fa8c58e. Report an issue: GitHub.

Appendix: source

Thrown at embassy-nxp/src/fmt.rs:223

    };
}

#[cfg(not(feature = "defmt"))]
#[collapse_debuginfo(yes)]
macro_rules! unwrap {
    ($arg:expr) => {
        match $crate::fmt::Try::into_result($arg) {
            ::core::result::Result::Ok(t) => t,
            ::core::result::Result::Err(e) => {
                ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
            }
        }
    };
    ($arg:expr, $($msg:expr),+ $(,)? ) => {
        match $crate::fmt::Try::into_result($arg) {
            ::core::result::Result::Ok(t) => t,
            ::core::result::Result::Err(e) => {
                ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
            }
        }
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;

pub trait Try {
    type Ok;
    type Error;
    fn into_result(self) -> Result<Self::Ok, Self::Error>;
}

impl<T> Try for Option<T> {
    type Ok = T;
    type Error = NoneError;

View on GitHub (pinned to 463a07b963)