Fix(gateway): Prevent crash on unhandled fetch rejections (close #3974)
This commit is contained in:
parent
5f4715acfc
commit
f4d567f10a
@ -87,6 +87,18 @@ describe("isTransientNetworkError", () => {
|
|||||||
expect(isTransientNetworkError(error)).toBe(true);
|
expect(isTransientNetworkError(error)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns true for fetch failed with unknown cause (Regression Test for #3974)", () => {
|
||||||
|
// Simulate Cause with NO code
|
||||||
|
const causeNoCode = new Error("Some obscure network error");
|
||||||
|
const errorNoCode = Object.assign(new TypeError("fetch failed"), { cause: causeNoCode });
|
||||||
|
expect(isTransientNetworkError(errorNoCode)).toBe(true);
|
||||||
|
|
||||||
|
// Simulate Cause with UNKNOWN code
|
||||||
|
const causeUnknown = Object.assign(new Error("Unknown"), { code: "UNKNOWN_123" });
|
||||||
|
const errorUnknown = Object.assign(new TypeError("fetch failed"), { cause: causeUnknown });
|
||||||
|
expect(isTransientNetworkError(errorUnknown)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("returns true for nested cause chain with network error", () => {
|
it("returns true for nested cause chain with network error", () => {
|
||||||
const innerCause = Object.assign(new Error("connection reset"), { code: "ECONNRESET" });
|
const innerCause = Object.assign(new Error("connection reset"), { code: "ECONNRESET" });
|
||||||
const outerCause = Object.assign(new Error("wrapper"), { cause: innerCause });
|
const outerCause = Object.assign(new Error("wrapper"), { cause: innerCause });
|
||||||
|
|||||||
@ -83,8 +83,14 @@ export function isTransientNetworkError(err: unknown): boolean {
|
|||||||
|
|
||||||
// "fetch failed" TypeError from undici (Node's native fetch)
|
// "fetch failed" TypeError from undici (Node's native fetch)
|
||||||
if (err instanceof TypeError && err.message === "fetch failed") {
|
if (err instanceof TypeError && err.message === "fetch failed") {
|
||||||
|
// Almost all cases of "fetch failed" are network related (DNS, timeout, connection refuse).
|
||||||
|
// If we can't determine otherwise, assume transient to prevent crashing the gateway.
|
||||||
const cause = getErrorCause(err);
|
const cause = getErrorCause(err);
|
||||||
if (cause) return isTransientNetworkError(cause);
|
if (cause) {
|
||||||
|
if (isTransientNetworkError(cause)) return true;
|
||||||
|
// If cause exists but isn't explicitly fatal, treat "fetch failed" as transient
|
||||||
|
if (!isFatalError(cause) && !isConfigError(cause)) return true;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user