• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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