| # Error handling |
| |
| Error handling represents one of the most important considerations when |
| implementing a Node.js native add-on. When an error occurs in your C++ code you |
| have to handle and dispatch it correctly. **node-addon-api** uses return values and |
| JavaScript exceptions for error handling. You can choose return values or |
| exception handling based on the mechanism that works best for your add-on. |
| |
| The `Napi::Error` is a persistent reference (for more info see: [`Napi::ObjectReference`](object_reference.md)) |
| to a JavaScript error object. Use of this class depends on whether C++ |
| exceptions are enabled at compile time. |
| |
| If C++ exceptions are enabled (for more info see: [Setup](setup.md)), then the |
| `Napi::Error` class extends `std::exception` and enables integrated |
| error-handling for C++ exceptions and JavaScript exceptions. |
| |
| Note, that due to limitations of the Node-API, if one attempts to cast the error object wrapping a primitive inside a C++ addon, the wrapped object |
| will be received instead. (With property `4bda9e7e-4913-4dbc-95de-891cbf66598e-errorVal` containing the primitive value thrown) |
| |
| The following sections explain the approach for each case: |
| |
| - [Handling Errors With C++ Exceptions](#exceptions) |
| - [Handling Errors With Maybe Type and C++ Exceptions Disabled](#noexceptions-maybe) |
| - [Handling Errors Without C++ Exceptions](#noexceptions) |
| |
| <a name="exceptions"></a> |
| |
| In most cases when an error occurs, the addon should do whatever cleanup is possible |
| and then return to JavaScript so that the error can be propagated. In less frequent |
| cases the addon may be able to recover from the error, clear the error and then |
| continue. |
| |
| ## Handling Errors With C++ Exceptions |
| |
| When C++ exceptions are enabled try/catch can be used to catch exceptions thrown |
| from calls to JavaScript and then they can either be handled or rethrown before |
| returning from a native method. |
| |
| If a node-addon-api call fails without executing any JavaScript code (for example due to |
| an invalid argument), then node-addon-api automatically converts and throws |
| the error as a C++ exception of type `Napi::Error`. |
| |
| If a JavaScript function called by C++ code via node-addon-api throws a JavaScript |
| exception, then node-addon-api automatically converts and throws it as a C++ |
| exception of type `Napi::Error` on return from the JavaScript code to the native |
| method. |
| |
| If a C++ exception of type `Napi::Error` escapes from a Node-API C++ callback, then |
| the Node-API wrapper automatically converts and throws it as a JavaScript exception. |
| |
| If other types of C++ exceptions are thrown, node-addon-api will either abort |
| the process or wrap the exception in an `Napi::Error` in order to throw it as a |
| JavaScript exception. This behavior is determined by which node-gyp dependency |
| used: |
| |
| - When using the `node_addon_api_except` dependency, only `Napi::Error` objects |
| will be handled. |
| - When using the `node_addon_api_except_all` dependency, all exceptions will be |
| handled. For exceptions derived from `std::exception`, an `Napi::Error` will be |
| created with the message of the exception's `what()` member function. For all |
| other exceptions, an `Napi::Error` will be created with a generic error message. |
| |
| On return from a native method, node-addon-api will automatically convert a pending |
| `Napi::Error` C++ exception to a JavaScript exception. |
| |
| When C++ exceptions are enabled try/catch can be used to catch exceptions thrown |
| from calls to JavaScript and then they can either be handled or rethrown before |
| returning from a native method. |
| |
| ## Examples with C++ exceptions enabled |
| |
| ### Throwing a C++ exception |
| |
| ```cpp |
| Env env = ... |
| throw Napi::Error::New(env, "Example exception"); |
| // other C++ statements |
| // ... |
| ``` |
| |
| The statements following the throw statement will not be executed. The exception |
| will bubble up as a C++ exception of type `Napi::Error`, until it is either caught |
| while still in C++, or else automatically propagated as a JavaScript exception |
| when returning to JavaScript. |
| |
| ### Propagating a Node-API C++ exception |
| |
| ```cpp |
| Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); |
| Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); |
| // other C++ statements |
| // ... |
| ``` |
| |
| The C++ statements following the call to the JavaScript function will not be |
| executed. The exception will bubble up as a C++ exception of type `Napi::Error`, |
| until it is either caught while still in C++, or else automatically propagated as |
| a JavaScript exception when returning to JavaScript. |
| |
| ### Handling a Node-API C++ exception |
| |
| ```cpp |
| Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); |
| Napi::Value result; |
| try { |
| result = jsFunctionThatThrows({ arg1, arg2 }); |
| } catch (const Error& e) { |
| cerr << "Caught JavaScript exception: " + e.what(); |
| } |
| ``` |
| |
| Since the exception was caught here, it will not be propagated as a JavaScript |
| exception. |
| |
| <a name="noexceptions-maybe"></a> |
| |
| ## Handling Errors With Maybe Type and C++ Exceptions Disabled |
| |
| If C++ exceptions are disabled (for more info see: [Setup](setup.md)), then the |
| `Napi::Error` class does not extend `std::exception`. This means that any calls to |
| node-addon-api functions do not throw a C++ exceptions. Instead, these node-api |
| functions that call into JavaScript are returning with `Maybe` boxed values. |
| In that case, the calling side should convert the `Maybe` boxed values with |
| checks to ensure that the call did succeed and therefore no exception is pending. |
| If the check fails, that is to say, the returning value is _empty_, the calling |
| side should determine what to do with `env.GetAndClearPendingException()` before |
| attempting to call another node-api (for more info see: [Env](env.md)). |
| |
| The conversion from the `Maybe` boxed value to the actual return value is |
| enforced by compilers so that the exceptions must be properly handled before |
| continuing. |
| |
| ## Examples with Maybe Type and C++ exceptions disabled |
| |
| ### Throwing a JS exception |
| |
| ```cpp |
| Napi::Env env = ... |
| Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException(); |
| return; |
| ``` |
| |
| After throwing a JavaScript exception, the code should generally return |
| immediately from the native callback, after performing any necessary cleanup. |
| |
| ### Propagating a Node-API JS exception |
| |
| ```cpp |
| Napi::Env env = ... |
| Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); |
| Maybe<Napi::Value> maybeResult = jsFunctionThatThrows({ arg1, arg2 }); |
| Napi::Value result; |
| if (!maybeResult.To(&result)) { |
| // The Maybe is empty, calling into js failed, cleaning up... |
| // It is recommended to return an empty Maybe if the procedure failed. |
| return result; |
| } |
| ``` |
| |
| If `maybeResult.To(&result)` returns false a JavaScript exception is pending. |
| To let the exception propagate, the code should generally return immediately |
| from the native callback, after performing any necessary cleanup. |
| |
| ### Handling a Node-API JS exception |
| |
| ```cpp |
| Napi::Env env = ... |
| Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); |
| Maybe<Napi::Value> maybeResult = jsFunctionThatThrows({ arg1, arg2 }); |
| if (maybeResult.IsNothing()) { |
| Napi::Error e = env.GetAndClearPendingException(); |
| cerr << "Caught JavaScript exception: " + e.Message(); |
| } |
| ``` |
| |
| Since the exception was cleared here, it will not be propagated as a JavaScript |
| exception after the native callback returns. |
| |
| <a name="noexceptions"></a> |
| |
| ## Handling Errors Without C++ Exceptions |
| |
| If C++ exceptions are disabled (for more info see: [Setup](setup.md)), then the |
| `Napi::Error` class does not extend `std::exception`. This means that any calls to |
| node-addon-api function do not throw a C++ exceptions. Instead, it raises |
| _pending_ JavaScript exceptions and returns an _empty_ `Napi::Value`. |
| The calling code should check `env.IsExceptionPending()` before attempting to use a |
| returned value, and may use methods on the `Napi::Env` class |
| to check for, get, and clear a pending JavaScript exception (for more info see: [Env](env.md)). |
| If the pending exception is not cleared, it will be thrown when the native code |
| returns to JavaScript. |
| |
| ## Examples with C++ exceptions disabled |
| |
| ### Throwing a JS exception |
| |
| ```cpp |
| Napi::Env env = ... |
| Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException(); |
| return; |
| ``` |
| |
| After throwing a JavaScript exception, the code should generally return |
| immediately from the native callback, after performing any necessary cleanup. |
| |
| ### Propagating a Node-API JS exception |
| |
| ```cpp |
| Napi::Env env = ... |
| Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); |
| Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); |
| if (env.IsExceptionPending()) { |
| Error e = env.GetAndClearPendingException(); |
| return e.Value(); |
| } |
| ``` |
| |
| If env.IsExceptionPending() returns true a JavaScript exception is pending. To |
| let the exception propagate, the code should generally return immediately from |
| the native callback, after performing any necessary cleanup. |
| |
| ### Handling a Node-API JS exception |
| |
| ```cpp |
| Napi::Env env = ... |
| Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); |
| Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); |
| if (env.IsExceptionPending()) { |
| Napi::Error e = env.GetAndClearPendingException(); |
| cerr << "Caught JavaScript exception: " + e.Message(); |
| } |
| ``` |
| |
| Since the exception was cleared here, it will not be propagated as a JavaScript |
| exception after the native callback returns. |
| |
| ## Calling Node-API directly from a **node-addon-api** addon |
| |
| **node-addon-api** provides macros for throwing errors in response to non-OK |
| `napi_status` results when calling [Node-API](https://nodejs.org/docs/latest/api/n-api.html) |
| functions from within a native addon. These macros are defined differently |
| depending on whether C++ exceptions are enabled or not, but are available for |
| use in either case. |
| |
| ### `NAPI_THROW(e, ...)` |
| |
| This macro accepts a `Napi::Error`, throws it, and returns the value given as |
| the last parameter. If C++ exceptions are enabled (by defining |
| `NAPI_CPP_EXCEPTIONS` during the build), the return value will be ignored. |
| |
| ### `NAPI_THROW_IF_FAILED(env, status, ...)` |
| |
| This macro accepts a `Napi::Env` and a `napi_status`. It constructs an error |
| from the `napi_status`, throws it, and returns the value given as the last |
| parameter. If C++ exceptions are enabled (by defining `NAPI_CPP_EXCEPTIONS` |
| during the build), the return value will be ignored. |
| |
| ### `NAPI_THROW_IF_FAILED_VOID(env, status)` |
| |
| This macro accepts a `Napi::Env` and a `napi_status`. It constructs an error |
| from the `napi_status`, throws it, and returns. |
| |
| ### `NAPI_FATAL_IF_FAILED(status, location, message)` |
| |
| This macro accepts a `napi_status`, a C string indicating the location where the |
| error occurred, and a second C string for the message to display. |