Retry Strategies
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
Section titled “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 4sAttempt 2: fails → wait 8sAttempt 3: fails → wait 16sAttempt 4: fails → wait 32sAttempt 5: fails → wait 64sAttempt 6: fails → exception raisedCustomizing Retry Behavior
Section titled “Customizing Retry Behavior”To adjust retry parameters, pass a configured strategy to the agent constructor.
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 ))In TypeScript, ModelRetryStrategy is the abstract base class. Use DefaultModelRetryStrategy for the standard configurable strategy:
import { Agent, DefaultModelRetryStrategy } from '@strands-agents/sdk'
const agent = new Agent({ retryStrategy: new DefaultModelRetryStrategy({ maxAttempts: 3 }),})Parameters
Section titled “Parameters”| 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. |
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
Section titled “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.
Configure backoff through ModelRetryStrategy parameters in Customizing Retry Behavior.
The TypeScript SDK ships three built-in strategies:
ExponentialBackoff(default): delay grows asbaseMs * multiplier^(attempt-1), capped atmaxMs.LinearBackoff: delay grows asbaseMs * attempt, capped atmaxMs.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.
import { Agent, DefaultModelRetryStrategy, ExponentialBackoff } from '@strands-agents/sdk'
const agent = new Agent({ retryStrategy: new DefaultModelRetryStrategy({ maxAttempts: 4, backoff: new ExponentialBackoff({ baseMs: 2_000, maxMs: 60_000, multiplier: 2, jitter: 'full', }), }),})Disabling Retries
Section titled “Disabling Retries”To disable automatic retries entirely:
from strands import Agent
agent = Agent( retry_strategy=None)import { Agent } from '@strands-agents/sdk'
const agent = new Agent({ retryStrategy: null,})When Retries Occur
Section titled “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
Section titled “Custom Retry Logic”For more control over retry decisions, such as validating model responses or handling additional error types, two paths are available:
- Subclass the retry strategy class to own the full decision shape and reuse hook plumbing.
- Use a hook on
AfterModelCallEventto setevent.retry = truedirectly.
Subclassing the Retry Strategy
Section titled “Subclassing the Retry Strategy”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.
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.
import { Agent, DefaultModelRetryStrategy } from '@strands-agents/sdk'
class TransientServiceError extends Error { readonly name = 'TransientServiceError'}
// Retry throttles (the default retryable set) plus our own transient error class.class WiderRetryStrategy extends DefaultModelRetryStrategy { protected override isRetryable(error: Error): boolean { return super.isRetryable(error) || error instanceof TransientServiceError }}
const agent = new Agent({ retryStrategy: new WiderRetryStrategy({ maxAttempts: 5 }),})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.
Using a Hook
Section titled “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.
import asynciofrom strands import Agentfrom 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()])The AfterModelCallEvent exposes attemptCount (1-indexed). Use it to bound retry counts without tracking state outside the hook.
import { Agent, AfterModelCallEvent } from '@strands-agents/sdk'
const agent = new Agent({})
let attempts = 0agent.addHook(AfterModelCallEvent, async (event) => { if (event.error && attempts < 3) { attempts += 1 await new Promise((resolve) => setTimeout(resolve, 2_000)) event.retry = true }})See Hooks for more examples.