fix(normalization-core): preserve non-Error object causes with extra keys in formatErrorMessage (#126654)

* fix(normalization-core): preserve non-Error object causes with extra keys

The cause-chain branch of formatErrorMessage called only
formatStatusAndCode(cause) with no stringifyUnknown fallback, while the
top-level branch used formatStatusAndCode(value) ?? stringifyUnknown(value).
formatStatusAndCode returns undefined for any object whose keys are not
exactly status/code, so a non-Error object cause carrying extra keys (e.g.
{ statusCode: 429 } or { status: 503, code: "UNAVAILABLE", requestId: "abc" })
was silently dropped — appendCauseMessage(undefined) no-op'd and the loop
broke, losing the diagnostic/retryable detail.

Mirror the top-level branch: appendCauseMessage(formatStatusAndCode(cause) ??
stringifyUnknown(cause)). Behavior-neutral for causes that already render;
restores the dropped detail for the asymmetric case. stringifyUnknown is a
local helper in the same file.

Closes #126652

Co-Authored-By: Claude <noreply@anthropic.com>

* test(normalization-core): assert structured cause metadata

---------

Co-authored-by: ruel225 <ruel225@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Altay <altay@hey.com>
This commit is contained in:
ruel225
2026-08-21 21:09:25 +08:00
committed by GitHub
parent efbcb57569
commit 587c7524e5
2 changed files with 17 additions and 2 deletions
@@ -55,9 +55,21 @@ describe("formatErrorMessage", () => {
expect(format(new Error("request failed", { cause: { status: 429 } }))).toBe(
"request failed | status=429 code=unknown",
);
// A non-Error cause carrying recognized status/code fields alongside extra
// keys used to be dropped entirely: formatStatusAndCode returns undefined
// for any object with keys beyond status/code, and the cause-chain branch
// had no stringifyUnknown fallback (unlike the top-level branch). The
// structured detail now survives instead of being swallowed.
expect(format(new Error("request failed", { cause: { statusCode: 429 } }))).toBe(
"request failed",
'request failed | {"statusCode":429}',
);
expect(
format(
new Error("request failed", {
cause: { status: 503, code: "UNAVAILABLE", requestId: "abc" },
}),
),
).toBe('request failed | {"status":503,"code":"UNAVAILABLE","requestId":"abc"}');
});
it("stringifies primitives and circular records without throwing", () => {
@@ -117,7 +117,10 @@ export function formatErrorMessage(value: unknown, options: FormatErrorMessageOp
appendCauseMessage(cause);
break;
} else {
appendCauseMessage(formatStatusAndCode(cause));
// Mirror the top-level branch: an object cause with keys beyond
// status/code makes formatStatusAndCode return undefined, so fall
// back to stringifyUnknown rather than dropping the cause entirely.
appendCauseMessage(formatStatusAndCode(cause) ?? stringifyUnknown(cause));
break;
}
}