1# Error handling 2 3Error handling represents one of the most important considerations when 4implementing a Node.js native add-on. When an error occurs in your C++ code you 5have to handle and dispatch it correctly. **node-addon-api** uses return values and 6JavaScript exceptions for error handling. You can choose return values or 7exception handling based on the mechanism that works best for your add-on. 8 9The `Napi::Error` is a persistent reference (for more info see: [`Napi::ObjectReference`](object_reference.md)) 10to a JavaScript error object. Use of this class depends on whether C++ 11exceptions are enabled at compile time. 12 13If C++ exceptions are enabled (for more info see: [Setup](setup.md)), then the 14`Napi::Error` class extends `std::exception` and enables integrated 15error-handling for C++ exceptions and JavaScript exceptions. 16 17Note, 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 18will be received instead. (With property `4bda9e7e-4913-4dbc-95de-891cbf66598e-errorVal` containing the primitive value thrown) 19 20The following sections explain the approach for each case: 21 22- [Handling Errors With C++ Exceptions](#exceptions) 23- [Handling Errors With Maybe Type and C++ Exceptions Disabled](#noexceptions-maybe) 24- [Handling Errors Without C++ Exceptions](#noexceptions) 25 26<a name="exceptions"></a> 27 28In most cases when an error occurs, the addon should do whatever cleanup is possible 29and then return to JavaScript so that the error can be propagated. In less frequent 30cases the addon may be able to recover from the error, clear the error and then 31continue. 32 33## Handling Errors With C++ Exceptions 34 35When C++ exceptions are enabled try/catch can be used to catch exceptions thrown 36from calls to JavaScript and then they can either be handled or rethrown before 37returning from a native method. 38 39If a node-addon-api call fails without executing any JavaScript code (for example due to 40an invalid argument), then node-addon-api automatically converts and throws 41the error as a C++ exception of type `Napi::Error`. 42 43If a JavaScript function called by C++ code via node-addon-api throws a JavaScript 44exception, then node-addon-api automatically converts and throws it as a C++ 45exception of type `Napi::Error` on return from the JavaScript code to the native 46method. 47 48If a C++ exception of type `Napi::Error` escapes from a Node-API C++ callback, then 49the Node-API wrapper automatically converts and throws it as a JavaScript exception. 50 51On return from a native method, node-addon-api will automatically convert a pending 52`Napi::Error` C++ exception to a JavaScript exception. 53 54When C++ exceptions are enabled try/catch can be used to catch exceptions thrown 55from calls to JavaScript and then they can either be handled or rethrown before 56returning from a native method. 57 58## Examples with C++ exceptions enabled 59 60### Throwing a C++ exception 61 62```cpp 63Env env = ... 64throw Napi::Error::New(env, "Example exception"); 65// other C++ statements 66// ... 67``` 68 69The statements following the throw statement will not be executed. The exception 70will bubble up as a C++ exception of type `Napi::Error`, until it is either caught 71while still in C++, or else automatically propagated as a JavaScript exception 72when returning to JavaScript. 73 74### Propagating a Node-API C++ exception 75 76```cpp 77Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); 78Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); 79// other C++ statements 80// ... 81``` 82 83The C++ statements following the call to the JavaScript function will not be 84executed. The exception will bubble up as a C++ exception of type `Napi::Error`, 85until it is either caught while still in C++, or else automatically propagated as 86a JavaScript exception when returning to JavaScript. 87 88### Handling a Node-API C++ exception 89 90```cpp 91Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); 92Napi::Value result; 93try { 94 result = jsFunctionThatThrows({ arg1, arg2 }); 95} catch (const Error& e) { 96 cerr << "Caught JavaScript exception: " + e.what(); 97} 98``` 99 100Since the exception was caught here, it will not be propagated as a JavaScript 101exception. 102 103<a name="noexceptions-maybe"></a> 104 105## Handling Errors With Maybe Type and C++ Exceptions Disabled 106 107If C++ exceptions are disabled (for more info see: [Setup](setup.md)), then the 108`Napi::Error` class does not extend `std::exception`. This means that any calls to 109node-addon-api functions do not throw a C++ exceptions. Instead, these node-api 110functions that call into JavaScript are returning with `Maybe` boxed values. 111In that case, the calling side should convert the `Maybe` boxed values with 112checks to ensure that the call did succeed and therefore no exception is pending. 113If the check fails, that is to say, the returning value is _empty_, the calling 114side should determine what to do with `env.GetAndClearPendingException()` before 115attempting to call another node-api (for more info see: [Env](env.md)). 116 117The conversion from the `Maybe` boxed value to the actual return value is 118enforced by compilers so that the exceptions must be properly handled before 119continuing. 120 121## Examples with Maybe Type and C++ exceptions disabled 122 123### Throwing a JS exception 124 125```cpp 126Napi::Env env = ... 127Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException(); 128return; 129``` 130 131After throwing a JavaScript exception, the code should generally return 132immediately from the native callback, after performing any necessary cleanup. 133 134### Propagating a Node-API JS exception 135 136```cpp 137Napi::Env env = ... 138Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); 139Maybe<Napi::Value> maybeResult = jsFunctionThatThrows({ arg1, arg2 }); 140Napi::Value result; 141if (!maybeResult.To(&result)) { 142 // The Maybe is empty, calling into js failed, cleaning up... 143 // It is recommended to return an empty Maybe if the procedure failed. 144 return result; 145} 146``` 147 148If `maybeResult.To(&result)` returns false a JavaScript exception is pending. 149To let the exception propagate, the code should generally return immediately 150from the native callback, after performing any necessary cleanup. 151 152### Handling a Node-API JS exception 153 154```cpp 155Napi::Env env = ... 156Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); 157Maybe<Napi::Value> maybeResult = jsFunctionThatThrows({ arg1, arg2 }); 158if (maybeResult.IsNothing()) { 159 Napi::Error e = env.GetAndClearPendingException(); 160 cerr << "Caught JavaScript exception: " + e.Message(); 161} 162``` 163 164Since the exception was cleared here, it will not be propagated as a JavaScript 165exception after the native callback returns. 166 167<a name="noexceptions"></a> 168 169## Handling Errors Without C++ Exceptions 170 171If C++ exceptions are disabled (for more info see: [Setup](setup.md)), then the 172`Napi::Error` class does not extend `std::exception`. This means that any calls to 173node-addon-api function do not throw a C++ exceptions. Instead, it raises 174_pending_ JavaScript exceptions and returns an _empty_ `Napi::Value`. 175The calling code should check `env.IsExceptionPending()` before attempting to use a 176returned value, and may use methods on the `Napi::Env` class 177to check for, get, and clear a pending JavaScript exception (for more info see: [Env](env.md)). 178If the pending exception is not cleared, it will be thrown when the native code 179returns to JavaScript. 180 181## Examples with C++ exceptions disabled 182 183### Throwing a JS exception 184 185```cpp 186Napi::Env env = ... 187Napi::Error::New(env, "Example exception").ThrowAsJavaScriptException(); 188return; 189``` 190 191After throwing a JavaScript exception, the code should generally return 192immediately from the native callback, after performing any necessary cleanup. 193 194### Propagating a Node-API JS exception 195 196```cpp 197Napi::Env env = ... 198Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); 199Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); 200if (env.IsExceptionPending()) { 201 Error e = env.GetAndClearPendingException(); 202 return e.Value(); 203} 204``` 205 206If env.IsExceptionPending() returns true a JavaScript exception is pending. To 207let the exception propagate, the code should generally return immediately from 208the native callback, after performing any necessary cleanup. 209 210### Handling a Node-API JS exception 211 212```cpp 213Napi::Env env = ... 214Napi::Function jsFunctionThatThrows = someValue.As<Napi::Function>(); 215Napi::Value result = jsFunctionThatThrows({ arg1, arg2 }); 216if (env.IsExceptionPending()) { 217 Napi::Error e = env.GetAndClearPendingException(); 218 cerr << "Caught JavaScript exception: " + e.Message(); 219} 220``` 221 222Since the exception was cleared here, it will not be propagated as a JavaScript 223exception after the native callback returns. 224 225## Calling Node-API directly from a **node-addon-api** addon 226 227**node-addon-api** provides macros for throwing errors in response to non-OK 228`napi_status` results when calling [Node-API](/docs/latest/api/n-api.html) 229functions from within a native addon. These macros are defined differently 230depending on whether C++ exceptions are enabled or not, but are available for 231use in either case. 232 233### `NAPI_THROW(e, ...)` 234 235This macro accepts a `Napi::Error`, throws it, and returns the value given as 236the last parameter. If C++ exceptions are enabled (by defining 237`NAPI_CPP_EXCEPTIONS` during the build), the return value will be ignored. 238 239### `NAPI_THROW_IF_FAILED(env, status, ...)` 240 241This macro accepts a `Napi::Env` and a `napi_status`. It constructs an error 242from the `napi_status`, throws it, and returns the value given as the last 243parameter. If C++ exceptions are enabled (by defining `NAPI_CPP_EXCEPTIONS` 244during the build), the return value will be ignored. 245 246### `NAPI_THROW_IF_FAILED_VOID(env, status)` 247 248This macro accepts a `Napi::Env` and a `napi_status`. It constructs an error 249from the `napi_status`, throws it, and returns. 250 251### `NAPI_FATAL_IF_FAILED(status, location, message)` 252 253This macro accepts a `napi_status`, a C string indicating the location where the 254error occurred, and a second C string for the message to display. 255