Skip to main content

Retry strategies

Reqon retries transient HTTP failures automatically. You configure the behaviour with a retry: block on a fetch step.

Retry configuration

Attach retry: to a get, post, or other fetch step:

get "/data" {
retry: {
maxAttempts: 5,
backoff: exponential,
initialDelay: 1000,
maxDelay: 60000
}
}

Options

OptionDescriptionDefault
maxAttemptsTotal attempts, including the first3
backoffStrategy: exponential, linear, constantexponential
initialDelayFirst retry delay (ms)1000
maxDelayCap on the delay between attempts (ms)30000
timeoutPer-attempt request timeout (ms)30000

There's no delay option; the delay comes from the backoff strategy and initialDelay.

What gets retried

Reqon retries automatically on:

  • 429 Too Many Requests — honouring the server's Retry-After header when present
  • 5x server errors

Retries apply to idempotent requests (get, put, delete), or any request carrying an idempotency key. post and patch aren't retried by default, since re-sending a write the server already committed could duplicate data.

Other responses behave differently:

  • 401 Unauthorized triggers a one-time token refresh when the source has a refresh token, then retries once.
  • Other 4xx responses (400, 403, 404, 422) throw immediately and fail the action — retrying won't help.

When attempts are exhausted, the request throws and the action fails (or surfaces to a paused or failed execution you can resume).

Backoff strategies

Exponential backoff

Best for most APIs. The delay doubles after each attempt:

get "/data" {
retry: {
maxAttempts: 5,
backoff: exponential,
initialDelay: 1000
}
}

Timeline:

Attempt 1: immediate
Attempt 2: wait 1000ms (1s)
Attempt 3: wait 2000ms (2s)
Attempt 4: wait 4000ms (4s)
Attempt 5: wait 8000ms (8s)

Linear backoff

The delay increases by a fixed amount:

get "/data" {
retry: {
maxAttempts: 5,
backoff: linear,
initialDelay: 2000
}
}

Timeline:

Attempt 1: immediate
Attempt 2: wait 2000ms (2s)
Attempt 3: wait 4000ms (4s)
Attempt 4: wait 6000ms (6s)
Attempt 5: wait 8000ms (8s)

Constant backoff

The same delay every time:

get "/data" {
retry: {
maxAttempts: 5,
backoff: constant,
initialDelay: 5000
}
}

Timeline:

Attempt 1: immediate
Attempt 2: wait 5000ms (5s)
Attempt 3: wait 5000ms (5s)
Attempt 4: wait 5000ms (5s)
Attempt 5: wait 5000ms (5s)

Maximum delay

maxDelay prevents extremely long waits:

get "/data" {
retry: {
maxAttempts: 10,
backoff: exponential,
initialDelay: 1000,
maxDelay: 30000 // Cap at 30 seconds
}
}

Without a cap, exponential backoff grows quickly:

Attempt 8: wait 128000ms (2+ min)
Attempt 9: wait 256000ms (4+ min)

With maxDelay: 30000:

Attempt 8: wait 30000ms (30s)
Attempt 9: wait 30000ms (30s)

Per-attempt timeout

timeout aborts a single attempt that hangs, so a slow request doesn't block the whole retry budget:

get "/data" {
retry: {
maxAttempts: 3,
timeout: 10000 // Give up on an attempt after 10s, then retry
}
}

Retrying on response contents

The fetch retry: block handles HTTP-level failures. To re-run work based on a successful response body — for example, an API that returns { pending: true } while a job finishes — use the retry flow directive in a match arm. That replays the whole action:

action FetchResult {
get "/jobs/123"

match response {
_ where response.pending -> retry { maxAttempts: 5, backoff: exponential, initialDelay: 1000 },
_ -> store response -> results { key: .id }
}
}

See Flow control directives for the directive form.

Choosing the right strategy

ScenarioRecommended strategy
General API errorsExponential, 3-5 attempts
Rate limitingExponential, longer initial delay
TimeoutsLinear, medium delays
Flaky networkConstant, short delays
Critical operationsExponential with higher maxAttempts

Scheduled missions

Retrying a whole mission run is separate from request-level retries. A schedule's retry block uses different keys — maxRetries and delaySeconds:

schedule: every 1 hours {
retry: {
maxRetries: 3,
delaySeconds: 60
}
}

See Scheduling overview for details.

Best practices

Start small, increase gradually

retry: {
maxAttempts: 5,
backoff: exponential,
initialDelay: 1000, // Start small
maxDelay: 60000 // Cap at a reasonable max
}

Be respectful to APIs

// Good: back off generously
retry: {
maxAttempts: 5,
backoff: exponential,
initialDelay: 5000
}

// Risky: aggressive retries
retry: {
maxAttempts: 100,
backoff: constant,
initialDelay: 100
}

Troubleshooting

Retries not happening

Retries apply to 429 and 5xx on idempotent requests. A post or patch isn't retried unless it carries an idempotency key, and 4xx responses other than 401 throw immediately:

// Retried automatically on 429 / 5xx
get "/data" {
retry: { maxAttempts: 3 }
}

Too many retries

Lower maxAttempts or cap the wait with maxDelay:

retry: {
maxAttempts: 3,
maxDelay: 30000
}