# Timeouts and retries

> For the complete documentation index, see [llms.txt](https://docs.lertha.com/llms.txt).
> Any documentation page is available as raw Markdown by appending `.md` to its URL.

> Two budgets and one retry policy cover every failure mode.

## 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

```kotlin
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.
