From 7f5013eee3931a74ce9d9cc2a98b74eb587c0988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0?= Date: Tue, 5 Aug 2025 13:53:36 +0900 Subject: [PATCH] feat(napi): add `Error.cause` support to `napi::Error` (#2829) Co-authored-by: LongYinan --- .../bindgen_runtime/js_values/stream/read.rs | 1 + crates/napi/src/error.rs | 32 ++++++++++++++++++ crates/napi/src/threadsafe_function.rs | 1 + .../__tests__/__snapshots__/values.spec.ts.md | 2 ++ .../__snapshots__/values.spec.ts.snap | Bin 7423 -> 7432 bytes examples/napi/__tests__/values.spec.ts | 4 +++ examples/napi/example.wasi-browser.js | 1 + examples/napi/example.wasi.cjs | 1 + examples/napi/index.cjs | 1 + examples/napi/index.d.cts | 2 ++ examples/napi/src/error.rs | 7 ++++ 11 files changed, 52 insertions(+) diff --git a/crates/napi/src/bindgen_runtime/js_values/stream/read.rs b/crates/napi/src/bindgen_runtime/js_values/stream/read.rs index 2b5fbb68..a8d39e45 100644 --- a/crates/napi/src/bindgen_runtime/js_values/stream/read.rs +++ b/crates/napi/src/bindgen_runtime/js_values/stream/read.rs @@ -497,6 +497,7 @@ impl futures_core::Stream for Reader { *chunk = Err(Error { status: Status::GenericFailure, reason: "".to_string(), + cause: None, maybe_raw: error_ref, maybe_env: cx.env.0, }); diff --git a/crates/napi/src/error.rs b/crates/napi/src/error.rs index 233e6c19..95914ac2 100644 --- a/crates/napi/src/error.rs +++ b/crates/napi/src/error.rs @@ -12,6 +12,7 @@ use serde::{de, ser}; #[cfg(feature = "serde-json")] use serde_json::Error as SerdeJSONError; +use crate::bindgen_runtime::JsObjectValue; use crate::{bindgen_runtime::ToNapiValue, check_status, sys, Env, JsValue, Status, Unknown}; pub type Result = std::result::Result>; @@ -22,6 +23,7 @@ pub type Result = std::result::Result>; pub struct Error = Status> { pub status: S, pub reason: String, + pub cause: Option>, // Convert raw `JsError` into Error pub(crate) maybe_raw: sys::napi_ref, pub(crate) maybe_env: sys::napi_env, @@ -47,6 +49,12 @@ impl> Drop for Error { } } +impl> Error { + pub fn set_cause(&mut self, cause: Error) { + self.cause = Some(Box::new(cause)); + } +} + impl> std::fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -134,10 +142,17 @@ impl From> for Error { let maybe_error_message = value .coerce_to_string() .and_then(|a| a.into_utf8().and_then(|a| a.into_owned())); + let maybe_cause: Option> = value + .coerce_to_object() + .and_then(|obj| obj.get_named_property::("cause")) + .map(|cause| Box::new(cause.into())) + .ok(); + if let Ok(error_message) = maybe_error_message { return Self { status: Status::GenericFailure, reason: error_message, + cause: maybe_cause, maybe_raw: result, maybe_env, }; @@ -146,6 +161,7 @@ impl From> for Error { Self { status: Status::GenericFailure, reason: "".to_string(), + cause: maybe_cause, maybe_raw: result, maybe_env, } @@ -174,6 +190,7 @@ impl> Error { Error { status, reason: reason.to_string(), + cause: None, maybe_raw: ptr::null_mut(), maybe_env: ptr::null_mut(), } @@ -183,6 +200,7 @@ impl> Error { Error { status, reason: "".to_owned(), + cause: None, maybe_raw: ptr::null_mut(), maybe_env: ptr::null_mut(), } @@ -200,6 +218,7 @@ impl + Clone> Error { Ok(Self { status: self.status.clone(), reason: self.reason.to_string(), + cause: None, maybe_raw: self.maybe_raw, maybe_env: self.maybe_env, }) @@ -211,6 +230,7 @@ impl Error { Error { status: Status::GenericFailure, reason: reason.into(), + cause: None, maybe_raw: ptr::null_mut(), maybe_env: ptr::null_mut(), } @@ -222,6 +242,7 @@ impl From for Error { Error { status: Status::GenericFailure, reason: format!("{error}"), + cause: None, maybe_raw: ptr::null_mut(), maybe_env: ptr::null_mut(), } @@ -233,6 +254,7 @@ impl From for Error { Error { status: Status::GenericFailure, reason: format!("{error}"), + cause: None, maybe_raw: ptr::null_mut(), maybe_env: ptr::null_mut(), } @@ -382,6 +404,16 @@ macro_rules! impl_object_methods { debug_assert!(create_reason_status == sys::Status::napi_ok); let create_error_status = unsafe { $kind(env, error_code, reason_string, &mut js_error) }; debug_assert!(create_error_status == sys::Status::napi_ok); + if let Some(cause_error) = self.0.cause.take() { + let cause = ToNapiValue::to_napi_value(env, *cause_error) + .expect("Convert cause Error to napi_value should never error"); + let set_cause_status = + unsafe { sys::napi_set_named_property(env, js_error, c"cause".as_ptr().cast(), cause) }; + debug_assert!( + set_cause_status == sys::Status::napi_ok, + "Set cause property failed" + ); + } js_error } diff --git a/crates/napi/src/threadsafe_function.rs b/crates/napi/src/threadsafe_function.rs index 1729bb29..689831a4 100644 --- a/crates/napi/src/threadsafe_function.rs +++ b/crates/napi/src/threadsafe_function.rs @@ -755,6 +755,7 @@ unsafe extern "C" fn call_js_cb< Err(Error { maybe_raw: error_reference, maybe_env: raw_env, + cause: None, status: Status::from(raw_status), reason, }) diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.md b/examples/napi/__tests__/__snapshots__/values.spec.ts.md index 5284748a..c34986b8 100644 --- a/examples/napi/__tests__/__snapshots__/values.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/values.spec.ts.md @@ -986,6 +986,8 @@ Generated by [AVA](https://avajs.dev). ␊ export declare function throwError(): void␊ ␊ + export declare function throwErrorWithCause(): void␊ + ␊ export declare function throwSyntaxError(error: string, code?: string | undefined | null): void␊ ␊ export declare function toJsObj(): object␊ diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap index 86b50884a64bcb18959e47be034b7750224ac4a2..86d24628c6c1fe8eeb8939b8d1756efdf84f81e7 100644 GIT binary patch delta 7315 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 delta 7286 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 Q4SBQrUzrJ3h-iZV0Fwg&>Hq)$ diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index 092f89a6..fdeba47b 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -58,6 +58,7 @@ import { mapOption, readFile, throwError, + throwErrorWithCause, jsErrorCallback, customStatusCode, panic, @@ -872,6 +873,9 @@ test('Option', (t) => { test('Result', (t) => { t.throws(() => throwError(), void 0, 'Manual Error') + const errorWithCause = t.throws(() => throwErrorWithCause()) + t.is(errorWithCause?.message, 'Manual Error') + t.is((errorWithCause?.cause as Error)?.message, 'Inner Error') if (!process.env.SKIP_UNWIND_TEST) { t.throws(() => panic(), void 0, `Don't panic`) } diff --git a/examples/napi/example.wasi-browser.js b/examples/napi/example.wasi-browser.js index 10913a71..34dfa368 100644 --- a/examples/napi/example.wasi-browser.js +++ b/examples/napi/example.wasi-browser.js @@ -343,6 +343,7 @@ export const threadsafeFunctionThrowError = __napiModule.exports.threadsafeFunct export const threadsafeFunctionThrowErrorWithStatus = __napiModule.exports.threadsafeFunctionThrowErrorWithStatus export const throwAsyncError = __napiModule.exports.throwAsyncError export const throwError = __napiModule.exports.throwError +export const throwErrorWithCause = __napiModule.exports.throwErrorWithCause export const throwSyntaxError = __napiModule.exports.throwSyntaxError export const toJsObj = __napiModule.exports.toJsObj export const tsfnAsyncCall = __napiModule.exports.tsfnAsyncCall diff --git a/examples/napi/example.wasi.cjs b/examples/napi/example.wasi.cjs index 93604b57..980680c4 100644 --- a/examples/napi/example.wasi.cjs +++ b/examples/napi/example.wasi.cjs @@ -388,6 +388,7 @@ module.exports.threadsafeFunctionThrowError = __napiModule.exports.threadsafeFun module.exports.threadsafeFunctionThrowErrorWithStatus = __napiModule.exports.threadsafeFunctionThrowErrorWithStatus module.exports.throwAsyncError = __napiModule.exports.throwAsyncError module.exports.throwError = __napiModule.exports.throwError +module.exports.throwErrorWithCause = __napiModule.exports.throwErrorWithCause module.exports.throwSyntaxError = __napiModule.exports.throwSyntaxError module.exports.toJsObj = __napiModule.exports.toJsObj module.exports.tsfnAsyncCall = __napiModule.exports.tsfnAsyncCall diff --git a/examples/napi/index.cjs b/examples/napi/index.cjs index 30cb15b5..edbf086d 100644 --- a/examples/napi/index.cjs +++ b/examples/napi/index.cjs @@ -673,6 +673,7 @@ module.exports.threadsafeFunctionThrowError = nativeBinding.threadsafeFunctionTh module.exports.threadsafeFunctionThrowErrorWithStatus = nativeBinding.threadsafeFunctionThrowErrorWithStatus module.exports.throwAsyncError = nativeBinding.throwAsyncError module.exports.throwError = nativeBinding.throwError +module.exports.throwErrorWithCause = nativeBinding.throwErrorWithCause module.exports.throwSyntaxError = nativeBinding.throwSyntaxError module.exports.toJsObj = nativeBinding.toJsObj module.exports.tsfnAsyncCall = nativeBinding.tsfnAsyncCall diff --git a/examples/napi/index.d.cts b/examples/napi/index.d.cts index 0ebf0011..4ab61bfe 100644 --- a/examples/napi/index.d.cts +++ b/examples/napi/index.d.cts @@ -947,6 +947,8 @@ export declare function throwAsyncError(): Promise export declare function throwError(): void +export declare function throwErrorWithCause(): void + export declare function throwSyntaxError(error: string, code?: string | undefined | null): void export declare function toJsObj(): object diff --git a/examples/napi/src/error.rs b/examples/napi/src/error.rs index 986fbe75..020535de 100644 --- a/examples/napi/src/error.rs +++ b/examples/napi/src/error.rs @@ -5,6 +5,13 @@ pub fn throw_error() -> Result<()> { Err(Error::new(Status::InvalidArg, "Manual Error".to_owned())) } +#[napi] +pub fn throw_error_with_cause() -> Result<()> { + let mut err = Error::new(Status::GenericFailure, "Manual Error".to_owned()); + err.set_cause(Error::new(Status::InvalidArg, "Inner Error".to_owned())); + Err(err) +} + #[napi(catch_unwind)] pub fn panic() { panic!("Don't panic");