Retry a Failed Request With Backoff#
Networks fail, services have bad minutes, and rate limits bite. Instead of failing the whole run, retry a few times with a growing delay. This is the difference between a brittle job and a reliable one.
The Script#
// retry.js
async function fetchWithRetry(url, attempts = 4) {
let delay = 1000;
let lastError = null;
for (let attempt = 1; attempt <= attempts; attempt++) {
try {
const res = await fetch(url);
if (res.ok) {
return res;
}
// 4xx responses (except 429) will not fix themselves.
if (res.status < 500 && res.status !== 429) {
const error = new Error(`Request failed: ${res.status}`);
error.retryable = false;
throw error;
}
lastError = new Error(`Request failed: ${res.status}`);
} catch (error) {
if (error.retryable === false || attempt === attempts) {
throw error;
}
lastError = error;
}
if (attempt === attempts) {
throw lastError;
}
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms`);
await new Promise((resolve) => setTimeout(resolve, delay));
delay *= 2;
}
}
const res = await fetchWithRetry("https://api.github.com/zen");
console.log(await res.text());
How It Works#
- The loop retries up to
attemptstimes. The delay starts at one second and doubles each time (backoff), giving a struggling service room to recover. - Client errors such as
404are not retried, since the same request will fail the same way. Rate limits (429) and server errors (5xx) are. - The last attempt rethrows, so a genuinely broken call still fails the run and shows up as an error.
Setup#
Keep the total time under the run time limit; with four attempts and backoff, this stays well within it.