Commit ed9ad59672c for nodejs
commit ed9ad59672c447727725d97ed98f87e4d525f8c4
Author: agape1225 <49804691+agape1225@users.noreply.github.com>
Date: Tue Sep 22 22:27:47 2026 +0900
util: show SuppressedError's error/suppressed properties on inspect
util.inspect() (and console.log()) silently dropped a SuppressedError's
`error` and `suppressed` properties, the two pieces of information
needed to actually debug a disposal failure. Both properties are
non-enumerable, so they never showed up without this.
formatError() already special-cases two other non-enumerable
"container" properties for the same reason: Error's `cause` (#41002)
and AggregateError's `errors` (#43646). Handle SuppressedError's
`error` and `suppressed` the same way; nested SuppressedErrors (from
multiple failed disposals) are shown recursively since inspect()
already recurses into any Error-valued property.
Fixes: https://github.com/nodejs/node/issues/66033
Signed-off-by: agape1225 <49804691+agape1225@users.noreply.github.com>
PR-URL: https://github.com/nodejs/node/pull/66159
Fixes: https://github.com/nodejs/node/issues/66033
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Jordan Harband <ljharb@gmail.com>
diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js
index 2e6c564cd3c..cf0a8892f8f 100644
--- a/lib/internal/util/inspect.js
+++ b/lib/internal/util/inspect.js
@@ -1990,6 +1990,16 @@ function formatError(err, constructor, tag, ctx, keys) {
// If errors is a getter that throws, we ignore the error.
}
+ // Print the wrapped errors of a SuppressedError.
+ if (ObjectPrototypeHasOwnProperty(err, 'error') &&
+ (keys.length === 0 || !ArrayPrototypeIncludes(keys, 'error'))) {
+ ArrayPrototypePush(keys, 'error');
+ }
+ if (ObjectPrototypeHasOwnProperty(err, 'suppressed') &&
+ (keys.length === 0 || !ArrayPrototypeIncludes(keys, 'suppressed'))) {
+ ArrayPrototypePush(keys, 'suppressed');
+ }
+
stack = improveStack(stack, constructor, name, tag);
// Ignore the error message if it's contained in the stack.
diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js
index ae5289cc721..c85ee620d63 100644
--- a/test/parallel/test-util-inspect.js
+++ b/test/parallel/test-util-inspect.js
@@ -735,6 +735,53 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324');
Error.stackTraceLimit = stackTraceLimit;
}
+{
+ // The `error` and `suppressed` properties of a SuppressedError should be
+ // shown during inspection, same as `cause` and AggregateError's `errors`.
+ const { stackTraceLimit } = Error;
+ Error.stackTraceLimit = 0;
+
+ const disposeError = new Error('dispose error');
+ const bodyError = new Error('body error');
+ const suppressedError = new SuppressedError(
+ disposeError,
+ bodyError,
+ 'An error was suppressed during disposal',
+ );
+
+ assert.strictEqual(
+ util.inspect(suppressedError),
+ '[SuppressedError: An error was suppressed during disposal] ' +
+ '{\n [error]: [Error: dispose error],\n [suppressed]: [Error: body error]\n}',
+ );
+
+ // Nested SuppressedErrors (multiple failed disposals) must recurse.
+ const outer = new SuppressedError(
+ new Error('second dispose error'),
+ suppressedError,
+ 'outer',
+ );
+ assert.strictEqual(
+ util.inspect(outer),
+ '[SuppressedError: outer] {\n' +
+ ' [error]: [Error: second dispose error],\n' +
+ ' [suppressed]: [SuppressedError: An error was suppressed during disposal] {\n' +
+ ' [error]: [Error: dispose error],\n' +
+ ' [suppressed]: [Error: body error]\n' +
+ ' }\n' +
+ '}',
+ );
+
+ const custom = new Error('No own error/suppressed property');
+ Object.setPrototypeOf(custom, suppressedError);
+ assert.strictEqual(
+ util.inspect(custom),
+ '[SuppressedError: No own error/suppressed property]',
+ );
+
+ Error.stackTraceLimit = stackTraceLimit;
+}
+
{
const tmp = Error.stackTraceLimit;
// Force stackTraceLimit = 0 for this test, but make it non-enumerable