embassy-rs/embassy · error

unwrap of ` ` failed

Error message

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

What it means

embassy-nxp's internal `unwrap!` macro wraps an expression whose type implements `fmt::Try` (Result/Option) and, on Err/None, panics with the stringified expression and the Debug value of the error. It exists so embassy code and user code can unwrap with the embassy logger formatting (using `core::panic!` so it works without std). The message interpolates both the source expression text and the error payload.

Solutions

  1. Read the `{:?}` error payload in the message to identify the underlying error kind, then fix its cause.
  2. Replace `unwrap!` with explicit `match`/`?` handling in application code once past bring-up.
  3. Use the two-argument form `unwrap!(expr, "context")` to add context to the panic while debugging.
  4. For embedded error types with no Debug (defmt/std mismatch), ensure the error type implements the right Debug/Format trait for your logging feature set.

Example fix

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

Strategy: try-catch

Validate before calling

// Replace blind unwrap with explicit handling at the call site:
if let Err(e) = i2c.blocking_write_read(addr, &mut cmd, &mut buf) {
    defmt::warn!("i2c write_read failed: {:?}", e);
    return Err(e.into());
}

Type guard

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

Try / catch

// Embassy `unwrap!` panics and cannot be caught in embedded; handle Results before they reach unwrap!:
match op {
    Ok(v) => use(v),
    Err(e) => { defmt::error!("op failed: {:?}", e); recover_or_reset(); }
}

Prevention

When it happens

Trigger: Any call site (in embassy-nxp or user code using `embassy_nxp::fmt::unwrap!`) where the wrapped expression evaluates to `Err(e)` (or `None`); the panic message shows the expression string and `{:?}` of the error.

Common situations: Unwrapping peripheral init or operation Results that can legitimately fail (I2C NACK, timeout, invalid configuration); relying on unwrap during bring-up and then hitting a real runtime error instead of handling it.

Related errors


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

Appendix: source

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

    };
}

#[cfg(feature = "defmt")]
#[collapse_debuginfo(yes)]
macro_rules! unwrap {
    ($($x:tt)*) => {
        ::defmt::unwrap!($($x)*)
    };
}

#[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;

View on GitHub (pinned to 463a07b963)