ExecutionConfig retry settings re-run retryable tool failures and guardrail validation errors with exponential backoff and jitter (for tools that have not declared themselves unsafe to re-run — see Retrying Non-Idempotent Tools). For tools, max_retry_limit / retry_* are translated into the effective RetryPolicy — the single tool-retry budget shared with ToolConfig(retry_policy=...).
Quick Start
1
Enable with defaults
Retries with backoff are automatic when you set
max_retry_limit:2
Fine-tune backoff
Use
ExecutionConfig for delay, factor, and jitter:How It Works
Total attempts =1 + max_retry_limit. Default max_retry_limit=2 → up to 3 attempts.
Delay: min(initial_delay × factor^(attempt−1), 60s) + random(0, jitter × base).
The translation maps max_retry_limit → max_attempts (= limit + 1), retry_initial_delay (seconds) → initial_delay_ms, retry_backoff_factor → backoff_factor, and retry_jitter (fraction) → jitter=True + jitter_factor. The translated policy sets max_delay_ms = max(60000, initial_delay_ms), so ExecutionConfig users keep the historical 60-second delay cap (a plain RetryPolicy defaults to a 30-second cap).
Choosing Your Settings
Configuration
These fields are one spelling of the tool-retry budget. An explicit
ToolConfig(retry_policy=...) (or per-tool @tool(retry_policy=...)) wins over the ExecutionConfig alias. See Tool Retry Policy.What Gets Retried
A wall-clock timeout is retried only when the tool is idempotent — unknown tools default to non-idempotent (safe). Opt in with
.idempotent = True; see Tool Timeouts → Retry-on-timeout is opt-in.A tool that explicitly declares itself unsafe (
restart_safe=False or .idempotent = False) is vetoed from every retryable error, not just timeouts. Merely sharing a name with an entry in MUTATING_TOOLS does not count as a declaration — undeclared tools always retry per policy. MUTATING_TOOLS still gates the wall-clock-timeout retry (see Tool Timeouts).Retrying Non-Idempotent Tools
Fixed in PR #4283. Between PRs #4257 and #4283, an undeclared tool whose name happened to match an entry in
MUTATING_TOOLS (e.g. write_file, store_memory, mkdir) silently ran once instead of retrying. Only explicit restart_safe=False / .idempotent = False declarations veto the retry now.Async parity fixed in PR #4299 (issue #4298). Before #4299,
@tool(restart_safe=False) / .idempotent = False was ignored on the async retry path — agent.achat() / agent.astart() / agent.execute_tool_async() re-ran the tool body up to max_attempts times (default: 3× on a card-charging tool, an email tool, a POST). The declaration was already honoured on the sync path. Both paths now run declared-unsafe tools exactly once.tool.idempotentset to aboolon the tool object.tool.restart_safe— the public@tool(restart_safe=...)/BaseTool.restart_safecontract.- When
ToolConfig(allow_global_tools=True)is set, a matching global-registry tool’sidempotent/restart_safeattribute (parity with idempotency lookup).
What is not a declaration: merely sharing a name with an entry in
MUTATING_TOOLS (a 73-name registry used elsewhere for the escalation loop guard) is not an author declaration and does not disable retries. To opt an unsafe tool out of retries, mark it explicitly with @tool(restart_safe=False) or .idempotent = False.This is the same declaration honoured by durable resume: an in-flight
restart_safe=False tool returns NotSafelyResumable, and a failing one is not re-driven here. See Durable Runs → Restart-Safety Contract.Task-scoped tools inherit the veto. A tool supplied only through the task’s
tools_override (never registered on the agent) still has its restart_safe=False / idempotent=False declaration honoured. Task-scoped tools shadow same-named agent tools, so if you attach a safer version at task time it wins over the agent-level tool. (PR #4299)Common Patterns
Disable retries:Best Practices
Set jitter for parallel agents
Set jitter for parallel agents
Jitter spreads retry timing across agents and reduces thundering-herd spikes on shared APIs.
Don't retry programming errors
Don't retry programming errors
ValueError, TypeError, and AttributeError are treated as code bugs and are not retried.Mark tools with side effects as non-idempotent
Mark tools with side effects as non-idempotent
A tool that sends, writes, charges, or deletes should declare
@tool(restart_safe=False) (or set .idempotent = False). The tool body runs before it can raise, so retrying after a transient error would duplicate the side effect. Declared-unsafe tools run exactly once regardless of max_retry_limit.Backoff is capped at 60s
Backoff is capped at 60s
Very large backoff factors cannot exceed a 60-second base delay per attempt. The cap only matters when retries actually happen — declared-unsafe tools do not retry.
One budget, not two
One budget, not two
For tools,
ExecutionConfig.retry_* is an alias translated into the effective RetryPolicy — the same budget as ToolConfig(retry_policy=...). Setting an explicit retry_policy overrides the ExecutionConfig spelling.Related
ExecutionConfig
Full execution configuration reference
Guardrails
Input and output validation
Loop Guardrails
Cap tool calls per turn
Structured LLM Errors
LLM-level retry and error handling

