Retries
When an attempt fails with a retryable outcome (a 5xx response, a timeout, a network error), cronpad waits and tries again, up to max_attempts tries in total. Waits grow exponentially by default and include random jitter so that many failing jobs don’t retry in lockstep. A fatal outcome (such as a 404) ends the run immediately.
Retry policy
"retry": {
"max_attempts": 3,
"backoff": "exponential",
"initial_interval": "10s",
"max_interval": "10m",
"multiplier": 2.0,
"jitter": 0.2
}
| Field | Default | Description |
|---|---|---|
max_attempts | 3 | Total tries including the first. 1 disables retries. Capped at 10 on cronpad Cloud. |
backoff | exponential | exponential or fixed. |
initial_interval | 10s | Wait before the first retry (and every retry with fixed). |
max_interval | 10m | Upper bound for any single wait, including Retry-After values. |
multiplier | 2.0 | Growth factor for exponential. |
jitter | 0.2 | Each wait is randomized by ±20%. 0 disables jitter. |
Server-wide defaults live in [retry.defaults].
Wait times
Retry n waits min(initial_interval × multiplier^(n−1), max_interval), then ±jitter, with the final wait capped at max_interval.
With max_attempts: 6 and the other defaults:
| Attempt | Waits before it (before jitter) | Elapsed since first attempt |
|---|---|---|
| 0 (first) | — | 0 |
| 1 | 10 s | ~10 s |
| 2 | 20 s | ~30 s |
| 3 | 40 s | ~1 m 10 s |
| 4 | 80 s | ~2 m 30 s |
| 5 | 160 s | ~5 m 10 s |
If the endpoint sends Retry-After with a 429 or 503, cronpad waits that long instead (capped at max_interval).
What gets retried
Whether an outcome is retryable depends on the sink:
- Webhooks:
retry_on, by default 5xx, 408, 429, network errors and timeouts. See Webhooks. - Commands:
retry_exit_codesand timeouts. See Commands.
While a run is retrying
- The run’s status is
deferred, withnext_attempt_atset. - Retries never delay the job’s next scheduled run, unless the job uses
overlap_policy: skip_if_running(Misfires and overlap). - Retries are durable: they’re queued in the queue file and tracked in the database, so a restart doesn’t lose them.
- Every attempt is listed on the run’s page with its outcome and response.
Retrying by hand
A run that ended failed can be retried with Retry on the run page or POST /api/v1/runs/{id}/retry. This creates a new run with trigger = retry_manual for the same scheduled time and a new run ID (and therefore a new idempotency key). Repeated manual-retry requests for that occurrence coalesce into the same retry run; an exhausted manual retry returns 409 conflict.