Skip to content
Docs menu / Retrying

Retrying

Retrying is appropriate for transient failures such as temporary network or service unavailability. It is not a substitute for handling permanent errors, and defects or interruptions are never retried.

retry

Effect.retry reruns an Effect after a typed failure. The source Effect is always evaluated once before the retry policy is applied.

Example (Retrying a Fixed Number of Times)

import { Data, Effect } from "effect"
class TemporaryError extends Data.TaggedError("TemporaryError")<{
readonly attempt: number
}> {}
let attempts = 0
const request = Effect.suspend(() => {
attempts++
return attempts < 3
? Effect.fail(new TemporaryError({ attempt: attempts }))
: Effect.succeed("ok")
})
const program = request.pipe(Effect.retry({ times: 5 }))
Effect.runSync(program) // => "ok"
attempts // => 3

times is the number of retries after the initial attempt. Therefore { times: 5 } allows at most six executions of the source Effect.

Retrying Selected Errors

The options object can combine:

  • while: retry while the predicate is true;
  • until: stop retrying when the predicate is true;
  • times: limit the number of retries;
  • schedule: control timing and additional stopping conditions.

Predicates may return either a boolean or an Effect.

Example (Retrying Only Transient Errors)

import { Data, Effect, Exit } from "effect"
class RequestError extends Data.TaggedError("RequestError")<{
readonly retryable: boolean
}> {}
let attempts = 0
const request = Effect.failSync(() => {
attempts++
return new RequestError({ retryable: attempts < 2 })
})
const program = request.pipe(
Effect.retry({
times: 5,
while: (error) => error.retryable,
}),
)
Effect.runSyncExit(program) // => Exit.fail(new RequestError({ retryable: false }))
attempts // => 2

Using a Schedule

A Schedule can define delays, backoff, jitter, and retry limits. Schedule.recurs(3), for example, permits three retries after the initial attempt.

import { Effect, Schedule } from "effect"
let attempts = 0
const request = Effect.suspend(() => {
attempts++
return attempts < 2 ? Effect.fail("temporary") : Effect.succeed("ok")
})
const program = request.pipe(Effect.retry(Schedule.recurs(3)))
Effect.runSync(program) // => "ok"

Use Effect.repeat instead when repetition depends on successful values rather than errors.

retryOrElse

Effect.retryOrElse uses a Schedule and runs a fallback Effect when the schedule is exhausted. The fallback receives the final error and the schedule’s output.

Example (Falling Back After Retries)

import { Effect, Schedule } from "effect"
let attempts = 0
const request = Effect.failSync(() => {
attempts++
return "unavailable"
})
const program = Effect.retryOrElse(
request,
Schedule.recurs(2),
(error, retries) => Effect.succeed(`${error} after ${retries} retries`),
)
Effect.runSync(program) // => "unavailable after 2 retries"
attempts // => 3