Timeouts and retries
Two budgets
| Option | Covers |
|---|---|
attemptTimeout | one attempt, from the moment a worker picks it up |
overallTimeout | the whole run: queue wait plus every attempt |
Unbounded retries without an overall budget are rejected by RunOptions, because such a run can
live forever. Either bound the attempts or bound the run.
For a flow, attemptTimeout is the budget of one attempt of the whole body and overallTimeout
the budget of all of them.
Retry policy
RetryPolicy(
maxAttempts = 5, // 0 = unbounded, then overallTimeout is required
initialInterval = 1.seconds,
backoffCoefficient = 2.0,
maxInterval = 100.seconds,
nonRetryableErrors = setOf("IllegalArgumentException"),
)
Retrying is the orchestrator's job, not yours. The handler just fails; the engine waits, then
schedules the next attempt, possibly on a different worker. A handler stops the retrying early
by throwing NonRetryableTaskError.
What ends a run
| Cause | State |
|---|---|
| attempts exhausted | Failed, with the last error's message |
| non-retryable error | Failed, on that attempt |
| attempt budget exceeded on the last attempt | TimedOut |
| overall budget exceeded | TimedOut |
| cancel delivered at a heartbeat | Cancelled |
| operator terminated it | Terminated; no handler code observed it |
Remember: RetryPolicy in RunOptions is the whole retry story.