MCPcopy
hub / github.com/sindresorhus/ky / #retryFromError

Method #retryFromError

source/core/Ky.ts:852–928  ·  view source on GitHub ↗
(error: unknown, function_: T)

Source from the content-addressed store, hash-verified

850 }
851
852 async #retryFromError<T extends (...arguments_: any) => Promise<any>>(error: unknown, function_: T): Promise<ReturnType<T> | Response | void> {
853 this.#returnedResponseFromBeforeRetryHook = false;
854
855 const retryDelay = Math.min(await this.#calculateRetryDelay(error), maxSafeTimeout);
856 const delayOptions = {signal: this.#userProvidedAbortSignal};
857
858 const remainingTimeout = this.#getRemainingTotalTimeout();
859 if (remainingTimeout !== undefined) {
860 if (remainingTimeout <= 0) {
861 throw new TimeoutError(this.request);
862 }
863
864 // If waiting would consume all remaining budget, time out without starting another request.
865 if (retryDelay >= remainingTimeout) {
866 await delay(remainingTimeout, delayOptions);
867 throw new TimeoutError(this.request);
868 }
869 }
870
871 // Only use user-provided signal for delay, not our internal abortController
872 await delay(retryDelay, delayOptions);
873
874 this.#throwIfTotalTimeoutExhausted();
875
876 // Apply custom request from forced retry before beforeRetry hooks
877 // Ensure the custom request has the correct managed signal for timeouts and user aborts
878 if (error instanceof ForceRetryError && error.customRequest) {
879 const customRequest = new globalThis.Request(error.customRequest, this.#options.signal ? {signal: this.#options.signal} : undefined);
880 // Replacement Requests are authoritative by design. Do not rewrite headers here,
881 // even for cross-origin retries. Callers using `ky.retry({request})` explicitly
882 // opted into the exact Request they constructed.
883 this.#assignRequest(customRequest);
884 }
885
886 for (const hook of this.#options.hooks.beforeRetry) {
887 let hookResult: Awaited<ReturnType<typeof hook>>;
888 try {
889 // eslint-disable-next-line no-await-in-loop
890 hookResult = await hook({
891 request: this.request,
892 options: this.#getNormalizedOptions(),
893 error: error as Error,
894 retryCount: this.#retryCount + 1,
895 });
896 } catch (hookError) {
897 // Preserve the original request error path (`throw error`) so beforeError hooks can still run.
898 if (hookError instanceof Error && hookError !== error) {
899 this.#beforeRetryHookErrors.add(hookError);
900 }
901
902 throw hookError;
903 }
904
905 if (isRequestInstance(hookResult)) {
906 // Same contract as `ky.retry({request})`: a Request returned from `beforeRetry`
907 // is used as-is rather than being sanitized or otherwise rewritten by Ky.
908 this.#assignRequest(hookResult);
909 break;

Callers 2

#retryMethod · 0.95
function_Method · 0.80

Calls 9

#calculateRetryDelayMethod · 0.95
#assignRequestMethod · 0.95
#getNormalizedOptionsMethod · 0.95
#retryMethod · 0.95
delayFunction · 0.85
isRequestInstanceFunction · 0.85
isResponseInstanceFunction · 0.85

Tested by

no test coverage detected