Skip to content

Commit cfaa1e2

Browse files
benjamingrruyadorno
authored andcommitted
process: unhandledRejection support more errors
Support cross realm errors where `instanceof` errors in our unhandledRejection warning to print better error with stack traces. PR-URL: #41682 Refs: #41676 Reviewed-By: Nitzan Uziely <[email protected]> Reviewed-By: Tierney Cyren <[email protected]>
1 parent 4339299 commit cfaa1e2

File tree

2 files changed

+35
-6
lines changed

2 files changed

+35
-6
lines changed

lib/internal/process/promises.js

+18-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
ArrayPrototypeShift,
66
Error,
77
ObjectDefineProperty,
8+
ObjectPrototypeHasOwnProperty,
89
SafeWeakMap,
910
} = primordials;
1011

@@ -79,6 +80,12 @@ function hasRejectionToWarn() {
7980
return tickInfo[kHasRejectionToWarn] === 1;
8081
}
8182

83+
function isErrorLike(o) {
84+
return typeof o === 'object' &&
85+
o !== null &&
86+
ObjectPrototypeHasOwnProperty(o, 'stack');
87+
}
88+
8289
function getUnhandledRejectionsMode() {
8390
const { getOptionValue } = require('internal/options');
8491
switch (getOptionValue('--unhandled-rejections')) {
@@ -179,14 +186,21 @@ function emitUnhandledRejectionWarning(uid, reason) {
179186
`(rejection id: ${uid})`
180187
);
181188
try {
182-
if (reason instanceof Error) {
189+
if (isErrorLike(reason)) {
183190
warning.stack = reason.stack;
184191
process.emitWarning(reason.stack, unhandledRejectionErrName);
185192
} else {
186193
process.emitWarning(
187194
noSideEffectsToString(reason), unhandledRejectionErrName);
188195
}
189-
} catch {}
196+
} catch {
197+
try {
198+
process.emitWarning(
199+
noSideEffectsToString(reason), unhandledRejectionErrName);
200+
} catch {
201+
// Ignore.
202+
}
203+
}
190204

191205
process.emitWarning(warning);
192206
}
@@ -232,7 +246,7 @@ function processPromiseRejections() {
232246
try {
233247
switch (unhandledRejectionsMode) {
234248
case kStrictUnhandledRejections: {
235-
const err = reason instanceof Error ?
249+
const err = isErrorLike(reason) ?
236250
reason : generateUnhandledRejectionError(reason);
237251
// This destroys the async stack, don't clear it after
238252
triggerUncaughtException(err, true /* fromPromise */);
@@ -259,7 +273,7 @@ function processPromiseRejections() {
259273
case kThrowUnhandledRejections: {
260274
const handled = emit(reason, promise, promiseInfo);
261275
if (!handled) {
262-
const err = reason instanceof Error ?
276+
const err = isErrorLike(reason) ?
263277
reason : generateUnhandledRejectionError(reason);
264278
// This destroys the async stack, don't clear it after
265279
triggerUncaughtException(err, true /* fromPromise */);

test/parallel/test-promise-unhandled-warn.js

+17-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
'use strict';
33

44
const common = require('../common');
5+
const assert = require('assert');
56

67
// Verify that ignoring unhandled rejection works fine and that no warning is
78
// logged.
@@ -12,11 +13,25 @@ new Promise(() => {
1213

1314
Promise.reject('test');
1415

16+
function lookForMeInStackTrace() {
17+
Promise.reject(new class ErrorLike {
18+
constructor() {
19+
Error.captureStackTrace(this);
20+
this.message = 'ErrorLike';
21+
}
22+
}());
23+
}
24+
lookForMeInStackTrace();
25+
1526
// Unhandled rejections trigger two warning per rejection. One is the rejection
1627
// reason and the other is a note where this warning is coming from.
17-
process.on('warning', common.mustCall(4));
28+
process.on('warning', common.mustCall((reason) => {
29+
if (reason.message.includes('ErrorLike')) {
30+
assert.match(reason.stack, /lookForMeInStackTrace/);
31+
}
32+
}, 6));
1833
process.on('uncaughtException', common.mustNotCall('uncaughtException'));
19-
process.on('rejectionHandled', common.mustCall(2));
34+
process.on('rejectionHandled', common.mustCall(3));
2035

2136
process.on('unhandledRejection', (reason, promise) => {
2237
// Handle promises but still warn!

0 commit comments

Comments
 (0)