API reference@evolu/commonTask › 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 AbortErrorerr(AbortError) — passed through, no retry, no wrapping
  • Task returns any other error → retry until schedule exhausted or retryable returns false → err(RetryError) with cause = 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")),
  ),
);

See

RetryOptions