> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowwarden.io/llms.txt
> Use this file to discover all available pages before exploring further.

# @RetryPolicy

> Automatic retry with exponential backoff for failed Change Stream event handlers

The `@RetryPolicy` annotation enables **automatic retry with exponential backoff** for failed handler invocations. When placed on a class alongside `@ChangeStream`, the framework retries the handler up to `maxAttempts` times with increasing delays between attempts.

## Attributes

| Attribute      | Type                           | Default   | Description                                                                       |
| -------------- | ------------------------------ | --------- | --------------------------------------------------------------------------------- |
| `maxAttempts`  | `int`                          | `3`       | Maximum number of attempts (including the initial one)                            |
| `initialDelay` | `String`                       | `"500ms"` | Delay before the first retry. Supports `"500ms"`, `"1s"`, `"1m"` format           |
| `maxDelay`     | `String`                       | `"30s"`   | Maximum delay cap                                                                 |
| `multiplier`   | `double`                       | `2.0`     | Multiplier applied to the delay between each retry                                |
| `retryOn`      | `Class<? extends Throwable>[]` | `{}`      | Exception types that trigger a retry. Empty = all exceptions (except `noRetryOn`) |
| `noRetryOn`    | `Class<? extends Throwable>[]` | See below | Exception types that should **never** trigger a retry                             |
| `jitter`       | `boolean`                      | `true`    | Adds random variation (±20%) to the computed delay                                |

<Note>
  `maxAttempts` includes the initial invocation. So `maxAttempts = 3` means **1 initial attempt + 2 retries**.
</Note>

### `noRetryOn` Defaults

By default, the following exceptions skip retry entirely — they are considered **programming errors** that retrying won't fix:

* `IllegalArgumentException`
* `NullPointerException`
* `ClassCastException`

`noRetryOn` always **takes precedence** over `retryOn`.

## Backoff Algorithm

The delay between retries is computed using exponential backoff with an optional jitter:

```
baseDelay   = initialDelay × (multiplier ^ (attemptNumber - 1))
cappedDelay = min(baseDelay, maxDelay)

if jitter = true:
    finalDelay = cappedDelay × random(0.8, 1.2)    // ±20%
else:
    finalDelay = cappedDelay
```

Using the defaults (`initialDelay = "500ms"`, `multiplier = 2.0`, `maxDelay = "30s"`) and **disabling jitter** to show pure exponential growth, the delays are:

| Attempt       | Delay        |
| ------------- | ------------ |
| 1 (initial)   | —            |
| 2 (1st retry) | 500ms        |
| 3 (2nd retry) | 1s           |
| 4             | 2s           |
| 5             | 4s           |
| 6             | 8s           |
| 7             | 16s          |
| 8+            | 30s (capped) |

## Head-of-Line Blocking

`@RetryPolicy` executes retries on the stream's processing thread. While a handler is being retried, FlowWarden does **not** advance to the next event — the entire stream is held until the current event has succeeded, exhausted all retries, or returned `ErrorAction.SKIP` / `ErrorAction.DLQ` from an `@OnError` handler.

A long retry sequence therefore blocks the stream for the **sum** of all backoff intervals (e.g. \~15 s with `maxAttempts = 5, initialDelay = "1s", multiplier = 2.0`).

This is a deliberate trade-off preserving strict in-order delivery. See [the retry guide](/guides/retry-and-dlq#head-of-line-blocking) for a worked example and the available workarounds.

## See Also

<CardGroup cols={2}>
  <Card title="Retry & DLQ Guide" icon="map" href="/guides/retry-and-dlq">
    Understand retry flows, exception filtering, tracking attempts, and best practices
  </Card>

  <Card title="@DeadLetterQueue" icon="box-archive" href="/reference/dead-letter-queue">
    Store events that exhaust all retries for later reprocessing
  </Card>

  <Card title="@Checkpoint" icon="bookmark" href="/reference/checkpoint">
    Resume token persistence for reliable stream recovery
  </Card>

  <Card title="ChangeStreamContext" icon="circle-info" href="/reference/change-stream-context">
    Runtime context including attempt number, event ID, and more
  </Card>
</CardGroup>
