Skip to content
Open
3 changes: 0 additions & 3 deletions src/js/internal/util/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -1781,9 +1781,6 @@ function formatError(err, constructor, tag, ctx, keys) {
const name = err.name != null ? String(err.name) : "Error";
let stack = getStackString(err);

//! temp fix for Bun losing the error name from inherited errors + extraneous ": " with no message
stack = stack.replace(/^Error: /, `${name}${err.message ? ": " : ""}`);

removeDuplicateErrorKeys(ctx, keys, err, stack);

if ("cause" in err && (keys.length === 0 || !ArrayPrototypeIncludes(keys, "cause"))) {
Expand Down
59 changes: 51 additions & 8 deletions test/js/node/util/node-inspect-tests/parallel/util-inspect.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -591,14 +591,7 @@
// Exceptions should print the error message, not '{}'.
{
[new Error(), new Error("FAIL"), new TypeError("FAIL"), new SyntaxError("FAIL")].forEach(err => {
assert(
//! temp bug workaround with replace()'s
util.inspect(err).startsWith(err.stack.replace(/^Error: /, err.message ? "$&" : "Error")),
`Expected "${util.inspect(err)}" to start with "${err.stack.replace(
/^Error: /,
err.message ? "$&" : "Error",
)}"`,
);
assert.strictEqual(util.inspect(err), err.stack);

Check warning on line 594 in test/js/node/util/node-inspect-tests/parallel/util-inspect.test.js

View check run for this annotation

Claude / Claude Code Review

Sibling '//! temp bug workaround' in util-format.test.js not restored

There's a sibling `//! temp bug workaround` for the same native stack-header bug at `test/js/node/util/node-inspect-tests/parallel/util-format.test.js:436` — `customError.stack.replace(/^Error/, "Custom$&")` — that this PR should also restore to the upstream `assert.strictEqual(util.format(customError), customError.stack)`. With the native header now correct, `customError.stack` already begins with `CustomError: bar`, so the anchored `/^Error/` never matches and the `.replace()` is a dead no-op;
Comment thread
claude[bot] marked this conversation as resolved.
});

assert.throws(
Expand Down Expand Up @@ -3189,6 +3182,56 @@
}
});

test("error inspect preserves stack header when name/message change after materialization", () => {
// Bun's native .stack already emits `${name}${message ? ": " + message : ""}` as the first line,
// so formatError must not rewrite it. These headers match Node's output for the same inputs.
const firstLine = e => util.inspect(e).split("\n")[0];

// message cleared after .stack was materialized: header is preserved verbatim
{
const err = new Error("msg");
void err.stack;
err.message = "";
assert.strictEqual(firstLine(err), "Error: msg");
}
{
const err = new Error("Error: nested");
void err.stack;
err.message = "";
assert.strictEqual(firstLine(err), "Error: Error: nested");
}

// user-assigned stack starting with "Error: " on an empty-message Error is preserved
{
const err = new Error();
err.stack = "Error: manually set\n at foo";
assert.strictEqual(firstLine(err), "Error: manually set");
}

// name changed after .stack was materialized: header is not rewritten to the new name
{
const err = new Error("x");
void err.stack;
err.name = "Renamed";
assert.strictEqual(firstLine(err), "Error: x");
}

// native header is correct for subclassed errors and empty-message errors
{
class Foo extends Error {
name = "Foo";
}
const err = new Foo("x");
assert.strictEqual(err.stack.split("\n")[0], "Foo: x");
assert.strictEqual(firstLine(err), "Foo: x");
}
{
const err = new Error();
assert.strictEqual(err.stack.split("\n")[0], "Error");
assert.strictEqual(firstLine(err), "Error");
}
});

// Utility functions
function runCallChecks(exitCode) {
if (exitCode !== 0) return;
Expand Down
Loading