Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,18 @@ export function createFetch(globalOptions: CreateFetchOptions = {}): $Fetch {
retries = isPayloadMethod(context.options.method) ? 0 : 1;
}

const responseCode = (context.response && context.response.status) || 500;
// When there is no response (network / connection error), bypass the
// status code check and always retry. Previously the code fell back to
// 500, which caused requests to silently stop being retried whenever
// callers supplied a custom retryStatusCodes array that excluded 500.
const hasResponse = !!(context.response && context.response.status);
const responseCode = hasResponse ? context.response!.status : undefined;
if (
retries > 0 &&
(Array.isArray(context.options.retryStatusCodes)
? context.options.retryStatusCodes.includes(responseCode)
: retryStatusCodes.has(responseCode))
(!hasResponse ||
(Array.isArray(context.options.retryStatusCodes)
? context.options.retryStatusCodes.includes(responseCode!)
: retryStatusCodes.has(responseCode!)))
) {
const retryDelay =
typeof context.options.retryDelay === "function"
Expand Down
27 changes: 27 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,33 @@ describe("ofetch", () => {
await expect(abortHandle()).rejects.toThrow(/aborted/);
});

it("network errors are retried even when retryStatusCodes excludes 500", async () => {
// Simulate a network-level failure (no HTTP response at all).
// Previously, ofetch fell back to status code 500 when context.response
// was undefined. A custom retryStatusCodes array that excluded 500 would
// therefore also suppress retries for connection errors.
let callCount = 0;
const networkError = new TypeError("Failed to fetch");
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => {
callCount++;
throw networkError;
};
try {
await $fetch("https://example.com", {
retry: 2,
// 500 is intentionally absent; only proxy-related codes are listed.
retryStatusCodes: [502, 503, 504],
});
} catch {
// expected to throw after exhausting retries
} finally {
globalThis.fetch = originalFetch;
}
// Initial attempt + 2 retries = 3 calls total.
expect(callCount).toBe(3);
});

it("passing request obj should return request obj in error", async () => {
const error = await $fetch(getURL("/403"), { method: "post" }).catch(
(error: any) => error
Expand Down