| # 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. |