---
title: Retry Strategies
tags: [event-loop, error-handling]
---

Model providers occasionally encounter errors such as rate limits, service unavailability, or network timeouts. By default, the agent retries throttled responses automatically with exponential backoff. The `Agent.retry_strategy` (Python) or `Agent.retryStrategy` (TypeScript) parameter lets you customize this behavior.

## Default Behavior

Without configuration, agents retry throttling errors up to 5 times (6 total attempts) with exponential backoff starting at 4 seconds:

```
Attempt 1: fails → wait 4s
Attempt 2: fails → wait 8s
Attempt 3: fails → wait 16s
Attempt 4: fails → wait 32s
Attempt 5: fails → wait 64s
Attempt 6: fails → exception raised
```

## Customizing Retry Behavior

To adjust retry parameters, pass a configured strategy to the agent constructor.

<Tabs>
<Tab label="Python">

```python
from strands import Agent, ModelRetryStrategy

agent = Agent(
    retry_strategy=ModelRetryStrategy(
        max_attempts=3,      # Total attempts (including first try)
        initial_delay=2,     # Seconds before first retry
        max_delay=60         # Cap on backoff delay
    )
)
```
</Tab>
<Tab label="TypeScript">

In TypeScript, `ModelRetryStrategy` is the abstract base class. Use `DefaultModelRetryStrategy` for the standard configurable strategy:

```typescript
--8<-- "user-guide/concepts/agents/retry-strategies_imports.ts:default_strategy_imports"

--8<-- "user-guide/concepts/agents/retry-strategies.ts:default_strategy"
```
</Tab>
</Tabs>

### Parameters

<Tabs>
<Tab label="Python">

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `max_attempts` | `int` | `6` | Total number of attempts including the initial call. Set to `1` to disable retries. |
| `initial_delay` | `float` | `4` | Seconds to wait before the first retry. Subsequent retries double this value. |
| `max_delay` | `float` | `128` | Maximum seconds to wait between retries. Caps the exponential growth. |

</Tab>
<Tab label="TypeScript">

`DefaultModelRetryStrategy` accepts:

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `maxAttempts` | `number` | `6` | Total number of attempts including the initial call. Must be `>= 1`. |
| `backoff` | `BackoffStrategy` | `new ExponentialBackoff({ baseMs: 4000, maxMs: 240000 })` | Computes delay between retries. See [Backoff Strategies](#backoff-strategies). |

</Tab>
</Tabs>

### Backoff Strategies

In TypeScript, the delay between retries is delegated to a `BackoffStrategy` passed via the `backoff` parameter. The Python SDK does not expose a separate backoff interface; configure exponential backoff through the `initial_delay` and `max_delay` parameters above.

<Tabs>
<Tab label="Python">

Configure backoff through `ModelRetryStrategy` parameters in [Customizing Retry Behavior](#customizing-retry-behavior).

</Tab>
<Tab label="TypeScript">

The TypeScript SDK ships three built-in strategies:

- `ExponentialBackoff` (default): delay grows as `baseMs * multiplier^(attempt-1)`, capped at `maxMs`.
- `LinearBackoff`: delay grows as `baseMs * attempt`, capped at `maxMs`.
- `ConstantBackoff`: same delay for every retry.

`ExponentialBackoff` and `LinearBackoff` accept a `jitter` option (`'none'`, `'full'`, `'equal'`, `'decorrelated'`). Default is `'full'` jitter, which spreads concurrent clients across the wait window. `ConstantBackoff` returns the configured delay unchanged.

```typescript
--8<-- "user-guide/concepts/agents/retry-strategies_imports.ts:custom_backoff_imports"

--8<-- "user-guide/concepts/agents/retry-strategies.ts:custom_backoff"
```

</Tab>
</Tabs>

## Disabling Retries

To disable automatic retries entirely:

<Tabs>
<Tab label="Python">

```python
from strands import Agent

agent = Agent(
    retry_strategy=None
)
```
</Tab>
<Tab label="TypeScript">

```typescript
--8<-- "user-guide/concepts/agents/retry-strategies_imports.ts:disable_imports"

--8<-- "user-guide/concepts/agents/retry-strategies.ts:disable"
```
</Tab>
</Tabs>

## When Retries Occur

Default retry strategies handle throttling errors (`ModelThrottledException` in Python, `ModelThrottledError` in TypeScript) raised by model providers for rate-limiting. Other exceptions propagate immediately without retry.

To extend or narrow the retryable set, see [Custom Retry Logic](#custom-retry-logic).

## Custom Retry Logic

For more control over retry decisions, such as validating model responses or handling additional error types, two paths are available:

1. Subclass the retry strategy class to own the full decision shape and reuse hook plumbing.
2. Use a hook on `AfterModelCallEvent` to set `event.retry = true` directly.

### Subclassing the Retry Strategy

<Tabs>
<Tab label="Python">

The Python SDK's `ModelRetryStrategy` is a concrete configurable class. To customize beyond its `max_attempts` / `initial_delay` / `max_delay` parameters, use the hook approach below.

</Tab>
<Tab label="TypeScript">

To extend or narrow which errors are retryable, subclass `DefaultModelRetryStrategy` and override `isRetryable`. The default implementation retries `ModelThrottledError` only; `super.isRetryable(error)` preserves that base behavior so you can opt additional error types in (or specific ones out) without reimplementing the rest of the retry policy.

```typescript
--8<-- "user-guide/concepts/agents/retry-strategies_imports.ts:custom_subclass_imports"

--8<-- "user-guide/concepts/agents/retry-strategies.ts:custom_subclass"
```

For full control over the retry decision (custom backoff state, attempt-aware logic), subclass the abstract `ModelRetryStrategy` directly and implement `computeRetryDecision`. The base class handles short-circuits, per-turn state reset, and sleeping between attempts.

A retry-strategy instance carries per-turn state and must not be shared across agents. Create a new instance per agent.

</Tab>
</Tabs>

### Using a Hook

Hooks are simpler when you only need to inspect a model error and request a retry. Set `event.retry = true` from an `AfterModelCallEvent` callback. Hooks do not introduce delays automatically; the example below adds a 2-second wait.

<Tabs>
<Tab label="Python">

```python
import asyncio
from strands import Agent
from strands.hooks import HookProvider, HookRegistry, AfterModelCallEvent

class CustomRetry(HookProvider):
    def __init__(self, max_retries: int = 3, delay: float = 2.0):
        self.max_retries = max_retries
        self.delay = delay
        self.attempts = 0

    def register_hooks(self, registry: HookRegistry) -> None:
        registry.add_callback(AfterModelCallEvent, self.maybe_retry)

    async def maybe_retry(self, event: AfterModelCallEvent) -> None:
        if event.exception and self.attempts < self.max_retries:
            self.attempts += 1
            await asyncio.sleep(self.delay)
            event.retry = True

agent = Agent(hooks=[CustomRetry()])
```
</Tab>
<Tab label="TypeScript">

The `AfterModelCallEvent` exposes `attemptCount` (1-indexed). Use it to bound retry counts without tracking state outside the hook.

```typescript
--8<-- "user-guide/concepts/agents/retry-strategies_imports.ts:hook_retry_imports"

--8<-- "user-guide/concepts/agents/retry-strategies.ts:hook_retry"
```
</Tab>
</Tabs>

See [Hooks](hooks.md#model-call-retry) for more examples.
