neon-bindings/neon · error
Failed to unwrap napi_external as Box
Error message
Failed to unwrap napi_external as Box<Any>
What it means
`JsBox::from_local` converts a raw N-API local handle back into a `JsBox<T>`. It expects the handle's associated external data to be a `Box<dyn Any>` holding the boxed Rust value; the `.expect` fires when `maybe_external_deref` returns None — the local is not an napi_external carrying Neon's boxed data. This is an internal invariant violation: the handle was not created by `JsBox::new`, so the cast is invalid.
Solutions
- Ensure the local handle originates from `JsBox::new`/Neon's boxing path
- Use checked conversion APIs (e.g. `cx.argument::<JsBox<T>>(i)`) that return an error instead of unwrapping
- Audit manual N-API/FFI code that attaches externals so it uses Neon's expected Box<Any> layout
Example fix
Only call `JsBox::from_local` (or APIs that go through it) on handles produced by `JsBox::new`; type-check with `cx.argument::<JsBox<T>>()` which validates instead of panicking.
Defensive patterns
Strategy: type-guard
Prevention
- Never construct napi_external values outside Neon's boxing helpers
- Prefer `downcast` (which returns Option) over `from_local` when the value's type is uncertain
When it happens
Trigger: `from_local` panics when unwrapping an `Option<Box<dyn Any>>` from `maybe_external_deref(env, local)` yields None — i.e. the raw local handle does not wrap a Neon-managed napi_external (wrong value type passed where a JsBox is expected).
Common situations: Passing a plain JS object, function, or a foreign external to an API that reconstructs a `JsBox<T>`; FFI/manual N-API code creating externals outside Neon; type confusion between native handles.
AI-assisted analysis of neon-bindings/neon@38960e4381 (2026-09-13).
Data as JSON: /api/errors/690e6c3f43247610.
Report an issue: GitHub.
Appendix: source
Thrown at crates/neon/src/types_impl/boxed.rs:214
fn downcast<Other: Value>(cx: &mut Cx, other: &Other) -> Option<Self> {
let local = other.to_local();
let data = unsafe { maybe_external_deref(cx.env(), local) };
// Attempt to downcast the `Option<&BoxAny>` to `Option<*const T>`
data.and_then(|v| v.downcast_ref())
.map(|raw_data| Self(JsBoxInner { local, raw_data }))
}
fn to_local(&self) -> raw::Local {
self.0.local
}
unsafe fn from_local(env: Env, local: raw::Local) -> Self {
let raw_data = unsafe { maybe_external_deref(env, local) }
.expect("Failed to unwrap napi_external as Box<Any>")
.downcast_ref()
.expect("Failed to downcast Any");
Self(JsBoxInner { local, raw_data })
}View on GitHub (pinned to 38960e4381)