MCPcopy
hub / github.com/promptfoo/promptfoo / fetchWithRetries

Function fetchWithRetries

src/util/fetch/index.ts:626–684  ·  view source on GitHub ↗
(
  url: RequestInfo,
  options: FetchOptions = {},
  timeout: number,
  maxRetries?: number,
)

Source from the content-addressed store, hash-verified

624}
625
626export async function fetchWithRetries(
627 url: RequestInfo,
628 options: FetchOptions = {},
629 timeout: number,
630 maxRetries?: number,
631): Promise<Response> {
632 const contextMaxRetries = getFetchRetryContextMaxRetries();
633 maxRetries = Math.max(0, maxRetries ?? contextMaxRetries ?? 4);
634
635 let lastErrorMessage: string | undefined;
636 const backoff = getEnvInt('PROMPTFOO_REQUEST_BACKOFF_MS', 5000);
637
638 for (let i = 0; i <= maxRetries; i++) {
639 let response;
640 try {
641 // Disable transient retries in fetchWithProxy to avoid double-retrying
642 response = await fetchWithTimeout(
643 url,
644 { ...options, disableTransientRetries: true },
645 timeout,
646 );
647
648 if (getEnvBool('PROMPTFOO_RETRY_5XX') && response.status >= 500 && response.status < 600) {
649 throw new Error(`Internal Server Error: ${response.status} ${response.statusText}`);
650 }
651
652 if (response && isRateLimited(response)) {
653 await handleRateLimitedResponse(response, url, i, maxRetries);
654 continue;
655 }
656
657 return response;
658 } catch (error) {
659 // Don't retry on abort - propagate immediately
660 if (error instanceof Error && error.name === 'AbortError') {
661 throw error;
662 }
663
664 // Structured rate-limit errors are already final (quota fail-fast or
665 // retries exhausted) and carry retry-after / reset metadata. Don't
666 // swallow them in the generic retry path.
667 if (error instanceof HttpRateLimitError) {
668 throw error;
669 }
670
671 const errorMessage = formatFetchErrorMessage(error);
672
673 logger.debug(
674 `Request to ${urlForLog(url)} failed (attempt #${i + 1}), retrying: ${errorMessage}`,
675 );
676 if (i < maxRetries) {
677 const waitTime = Math.pow(2, i) * (backoff + 1000 * Math.random());
678 await sleep(waitTime);
679 }
680 lastErrorMessage = errorMessage;
681 }
682 }
683 throw new Error(`Request failed after ${maxRetries} retries: ${lastErrorMessage}`);

Callers 15

rateLimit.test.tsFile · 0.90
fetch.test.tsFile · 0.90
fetchAndReadBodyFunction · 0.90
handleWebhookFunction · 0.90
callApiMethod · 0.90
callApiMethod · 0.90
callApiMethod · 0.90
createWebPageMethod · 0.90
checkPageFetchedMethod · 0.90
checkExfilTrackingFunction · 0.90
createWebPageFunction · 0.90
updateWebPageFunction · 0.90

Calls 9

getEnvIntFunction · 0.90
getEnvBoolFunction · 0.90
sleepFunction · 0.90
isRateLimitedFunction · 0.85
formatFetchErrorMessageFunction · 0.85
urlForLogFunction · 0.85
fetchWithTimeoutFunction · 0.70

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…