diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 4cff372b330ec1..31372eae06a532 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -642,8 +642,9 @@ function formatRaw(ctx, value, recurseTimes) { } base = dateToISOString(value); } else if (isError(value)) { + const prefix = getPrefix(constructor, '', 'Error'); // Make error with message first say the error. - base = formatError(value); + base = formatError(value, prefix); // Wrap the error in brackets in case it has no stack trace. const stackStart = base.indexOf('\n at'); if (stackStart === -1) { @@ -652,7 +653,7 @@ function formatRaw(ctx, value, recurseTimes) { // The message and the stack have to be indented as well! if (ctx.indentationLvl !== 0) { const indentation = ' '.repeat(ctx.indentationLvl); - base = formatError(value).replace(/\n/g, `\n${indentation}`); + base = formatError(value, prefix).replace(/\n/g, `\n${indentation}`); } if (keys.length === 0) return base; @@ -861,8 +862,18 @@ function formatPrimitive(fn, value, ctx) { return fn(value.toString(), 'symbol'); } -function formatError(value) { - return value.stack || errorToString(value); +function formatError(value, constructorName) { + if (value.stack) { + let stack = value.stack; + const parent = Object.getPrototypeOf(value); + if (!stack.startsWith(constructorName)) { + stack = stack.replace(parent, constructorName.trim()); + } else if (parent === null) { + stack = stack.replace('Error', constructorName.trim()); + } + return stack; + } + return errorToString(value); } function formatNamespaceObject(ctx, value, recurseTimes, keys) { diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 713bf047ddd345..af89a740cba00a 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -1772,3 +1772,37 @@ assert.strictEqual( }); assert.strictEqual(util.inspect(obj), '[Set: null prototype] { 1, 2 }'); } + +// error subclassing and null prototype checks +{ + const error = new Error('My Message'); + assert.ok(util.inspect(error).includes('Error: My Message')); + + Object.setPrototypeOf(error, null); + assert.ok(util.inspect(error) + .includes('[Error: null prototype]: My Message')); +} + +{ + class MyCustomError extends Error {} + const customErr = new MyCustomError('custom message'); + assert.ok(util.inspect(customErr) + .includes('MyCustomError: custom message')); + + Object.setPrototypeOf(customErr, null); + assert.ok(util.inspect(customErr) + .includes('[Error: null prototype]: custom message')); +} + +// Check custom errors with name `File` +// works +{ + class FileCustomError extends Error {} + const customErr = new FileCustomError(); + assert.ok(util.inspect(customErr) + .includes('FileCustomError')); + + Object.setPrototypeOf(customErr, null); + assert.ok(util.inspect(customErr) + .includes('[Error: null prototype]')); +}