Left alone, a single failed API call inside a workflow fails the whole execution. Retry and Catch are the two ASL fields that change that - one retries a state in place, the other routes a persistent failure somewhere useful instead of just stopping.
Both are declared directly in the state's definition, so error handling lives with the workflow, not scattered across calling code.
Add Retry for transient failures, worth a few automatic attempts. Add Catch for failures you want to route to a specific fallback state rather than fail the execution.
A reservation step retries on throttling, then falls back to a cleanup state on any error that survives the retries, including one the retries didn't cover.
Two Retry entries target different error types with different backoff schedules - throttling gets a fast, repeated retry; a timeout gets a slower, shorter one.
Catch with States.ALL is a fallback that runs only after Retry is exhausted (or for an error type Retry did not list at all).
ResultPath: "$.error" on the Catch preserves the original input alongside the caught error, instead of overwriting it.
The fallback state (NotifyReservationFailed) is a normal state - here another SDK integration, not necessarily a dead end.
Retry is a list of retriers, each matching one or more error names via ErrorEquals. When a state fails with a matching error, Step Functions waits IntervalSeconds, retries the state, and if it fails again, waits IntervalSeconds * BackoffRate before the next attempt, up to MaxAttempts. Retriers are evaluated in order, and only the first one whose ErrorEquals matches the actual error is used - so list more specific error names before a broad one.
An optional MaxDelaySeconds caps how large the backoff can grow, and JitterStrategy (FULL) adds randomness to avoid synchronized retry storms across many concurrent executions.
Catch is a list of catchers, each with ErrorEquals and a Next state to transition to if that error occurs. Catch only takes effect after any configured Retry has exhausted its attempts (or immediately, if no Retry matches). States.ALL in ErrorEquals matches any error, useful as a final catch-all after more specific catchers.
ResultPath on a catcher controls what the failed state's error output looks like to the fallback state - by default it replaces the entire input with the error, but setting ResultPath (for example "$.error") merges the error into the existing input instead, which usually matters if the fallback still needs the original data.
ErrorEquals can match Step Functions' own reserved error names (States.ALL, States.Timeout, States.TaskFailed, States.Permissions, States.Runtime) or specific error names/codes surfaced by an SDK-integration call or a Lambda function's thrown exception name. For SDK integrations, the error name generally reflects the underlying AWS API's exception (for example DynamoDB.ProvisionedThroughputExceededException), the same exception class boto3 and SDK v3 would raise for that same call.
A state can have both Retry and Catch. The order is always: attempt the state, and on failure, work through Retry entries until one matches and attempts are exhausted (or none match), then fall through to Catch entries. This lets you retry a handful of times for transient issues and only fall back to compensating logic once retries are truly spent.
Listing States.ALL before a specific error. Retriers and catchers match the first entry whose ErrorEquals fits, so a catch-all listed first shadows everything after it. Fix: order specific error names before States.ALL.
Expecting Retry to catch permission errors usefully.States.Permissions errors (missing IAM grants) will not resolve by retrying. Fix: fix the execution role's permissions; do not add a Retry for States.Permissions.
Overwriting the original input on Catch. Without ResultPath, the caught error becomes the entire input to the fallback state, discarding prior data. Fix: set ResultPath to merge the error alongside existing input.
No jitter on high-concurrency retries. Many executions failing together and retrying on the same schedule can synchronize into a retry storm. Fix: set JitterStrategy: "FULL" to randomize backoff timing.
Assuming retries are free. Standard workflows bill per state transition, and each retry attempt is a transition. Fix: bound MaxAttempts deliberately rather than leaving it high by default.
Confusing Retry order with Catch order. They are two separate lists evaluated at different points (during failure, and after retries are exhausted), not one combined list. Fix: treat Retry as "try again" and Catch as "give up and go somewhere else," and design each independently.
Retry re-attempts the same failing state in place, with a backoff schedule. Catch takes effect once retries are exhausted (or immediately if none match) and transitions to a different, fallback state.
Does States.ALL match everything?
Yes, in either Retry or Catch. Order matters - list specific error names before States.ALL so they get their own handling instead of being shadowed by the catch-all.
Can a state have both Retry and Catch?
Yes, and this is the common pattern: retry a few times for transient errors, then catch whatever still fails and route it to compensating logic.
What does ResultPath do on a Catch?
It controls where the caught error is placed in the state's output. Without it, the error replaces the entire input to the fallback state; with it set (for example to $.error), the original input is preserved alongside the error.
How do I avoid a retry storm across many executions?
Set JitterStrategy: "FULL" on the retrier so backoff delays are randomized instead of synchronized across concurrent executions failing at the same time.
How can I see which retries and catches fired in a past execution?
For Standard workflows, call DescribeExecution for the outcome and GetExecutionHistory for the full state-by-state trace, including retry attempts and any catch transition.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+). NzA2MjAyfDM5MTFvaWdjfG9pLnNlZGl1Z2Vkb2M=
Reviewed by Chris St. John·Last updated Jul 23, 2026