API reference › @evolu/common › Task › retry
function retry<T, E, D, Output>(
task: Task<T, E, D>,
schedule: Schedule<Output, E>,
__namedParameters?: RetryOptions<E, Output>,
): Task<T, RetryError<E>, D>;
Defined in: packages/common/src/Task.ts:2013
Wraps a Task with retry logic.
Retries the Task according to the Schedule's rules. Use RetryOptions.retryable to filter which errors should trigger retries.
All non-abort errors are wrapped in RetryError:
- Task succeeds →
ok(value) - Task returns AbortError →
err(AbortError)— passed through, no retry, no wrapping - Task returns any other error → retry until schedule exhausted or
retryablereturns false →err(RetryError)withcause= the last error
The RetryError is informative: "I tried N times, here's why I finally gave
up" — and cause contains the actual underlying error.
Example
import { exponential, jitter, maxDelay, retry, take } from "@evolu/common";
const fetchWithRetry = retry(
fetchData,
// A jittered, capped, limited exponential backoff.
jitter(1)(maxDelay("20s")(take(2)(exponential("100ms")))),
);
const result = await run(fetchWithRetry);
if (!result.ok) {
if (AbortError.is(result.error)) {
// Was aborted externally
} else {
// RetryError — failed after retrying
console.log(`Failed after ${result.error.attempts} attempts`);
console.log(`Last error:`, result.error.cause);
}
}
The schedule receives the error as input, enabling error-aware strategies like stopping on fatal errors:
import { whileScheduleInput } from "@evolu/common";
// Don't retry fatal errors
const smartRetry = retry(
fetchData,
whileScheduleInput((e: FetchError) => e.type !== "FatalError")(
take(5)(spaced("1s")),
),
);