diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index a39779838a43a6..e25b599e8cc276 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -890,7 +890,7 @@ function formatRaw(ctx, value, recurseTimes, typedArray) { return ctx.stylize(base, 'date'); } } else if (isError(value)) { - base = formatError(value, constructor, tag, ctx); + base = formatError(value, constructor, tag, ctx, keys); if (keys.length === 0 && protoProps === undefined) return base; } else if (isAnyArrayBuffer(value)) { @@ -1083,11 +1083,23 @@ function getFunctionBase(value, constructor, tag) { return base; } -function formatError(err, constructor, tag, ctx) { +function formatError(err, constructor, tag, ctx, keys) { const name = err.name != null ? String(err.name) : 'Error'; let len = name.length; let stack = err.stack ? String(err.stack) : ErrorPrototypeToString(err); + // Do not "duplicate" error properties that are already included in the output + // otherwise. + if (!ctx.showHidden && keys.length !== 0) { + for (const name of ['name', 'message', 'stack']) { + const index = keys.indexOf(name); + // Only hide the property in case it's part of the original stack + if (index !== -1 && stack.includes(err[name])) { + keys.splice(index, 1); + } + } + } + // A stack trace may contain arbitrary data. Only manipulate the output // for "regular errors" (errors that "look normal") for now. if (constructor === null || diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 5540acfee30247..622171ea52206f 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -697,6 +697,28 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324'); Error.stackTraceLimit = tmp; } +// Prevent enumerable error properties from being printed. +{ + let err = new Error(); + err.message = 'foobar'; + let out = util.inspect(err).split('\n'); + assert.strictEqual(out[0], 'Error: foobar'); + assert(out[out.length - 1].startsWith(' at ')); + // Reset the error, the stack is otherwise not recreated. + err = new Error(); + err.message = 'foobar'; + err.name = 'Unique'; + Object.defineProperty(err, 'stack', { value: err.stack, enumerable: true }); + out = util.inspect(err).split('\n'); + assert.strictEqual(out[0], 'Unique: foobar'); + assert(out[out.length - 1].startsWith(' at ')); + err.name = 'Baz'; + out = util.inspect(err).split('\n'); + assert.strictEqual(out[0], 'Unique: foobar'); + assert.strictEqual(out[out.length - 2], " name: 'Baz'"); + assert.strictEqual(out[out.length - 1], '}'); +} + // Doesn't capture stack trace. { function BadCustomError(msg) {