Skip to content

Commit 447a364

Browse files
RomneyDaaltaywtf
andauthored
fix(errors): dedupe identical messages when traversing error .cause chain (#84556)
Merged via squash. Prepared head SHA: 46aa27f Co-authored-by: RomneyDa <6581799+RomneyDa@users.noreply.github.com> Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com> Reviewed-by: @altaywtf
1 parent 950e5c8 commit 447a364

2 files changed

Lines changed: 18 additions & 4 deletions

File tree

src/infra/errors.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ describe("error helpers", () => {
8888
expect(formatted).toBe("error A | error B");
8989
});
9090

91+
it("dedupes repeated cause messages while preserving deeper distinct causes", () => {
92+
const rootCause = new Error("provider auth lookup failed");
93+
const inner = new Error('No API key found for provider "openai-codex".', { cause: rootCause });
94+
const wrapper = new Error(inner.message, { cause: inner });
95+
expect(formatErrorMessage(wrapper)).toBe(`${inner.message} | ${rootCause.message}`);
96+
});
97+
9198
it("redacts sensitive tokens from formatted error messages", () => {
9299
const token = "sk-abcdefghijklmnopqrstuv";
93100
const formatted = formatErrorMessage(new Error(`Authorization: Bearer ${token}`));

src/infra/errors.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,22 @@ export function formatErrorMessage(err: unknown): string {
7272
// Traverse .cause chain to include nested error messages (e.g. grammY HttpError wraps network errors in .cause)
7373
let cause: unknown = err.cause;
7474
const seen = new Set<unknown>([err]);
75+
// Skip causes that repeat a message already emitted (e.g. coerceToFailoverError).
76+
const seenMessages = new Set<string>([formatted]);
77+
const appendCauseMessage = (message: string): void => {
78+
if (!message || seenMessages.has(message)) {
79+
return;
80+
}
81+
formatted += ` | ${message}`;
82+
seenMessages.add(message);
83+
};
7584
while (cause && !seen.has(cause)) {
7685
seen.add(cause);
7786
if (cause instanceof Error) {
78-
if (cause.message) {
79-
formatted += ` | ${cause.message}`;
80-
}
87+
appendCauseMessage(cause.message);
8188
cause = cause.cause;
8289
} else if (typeof cause === "string") {
83-
formatted += ` | ${cause}`;
90+
appendCauseMessage(cause);
8491
break;
8592
} else {
8693
break;

0 commit comments

Comments
 (0)