Skill 116 · AWS Step Functions
Subchapter 116.3
references/error-handling.mdMarkdown6 KBView on GitHub
When a state encounters an error, Step Functions defaults to failing the entire execution. You can override this with Retry (retry the failed state) and Catch (transition to a fallback state). Retry and Catch are available on: Task, Parallel, and Map states.
Errors are identified by case-sensitive strings. Step Functions defines these built-in error codes:
| Error Code | Description |
|---|---|
States.ALL | Wildcard — matches any error |
States.Timeout | Task exceeded TimeoutSeconds or missed heartbeat |
States.HeartbeatTimeout | Task missed heartbeat interval |
States.TaskFailed | Task failed during execution |
States.Permissions | Insufficient privileges |
States.QueryEvaluationError | JSONata expression evaluation failed |
States.BranchFailed | A Parallel state branch failed |
States.NoChoiceMatched | No Choice rule matched and no Default |
States.ExceedToleratedFailureThreshold | Map state exceeded failure tolerance |
States.ItemReaderFailed | Map state ItemReader failed |
States.ResultWriterFailed | Map state ResultWriter failed |
Custom error names are allowed but must NOT start with States..
The Retry field is an array of Retrier objects. The interpreter scans retriers in order and uses the first one whose ErrorEquals matches.
| Field | Type | Default | Description |
|---|---|---|---|
ErrorEquals | string[] | Required | Error names to match |
IntervalSeconds | integer | 1 | Seconds before first retry |
MaxAttempts | integer | 3 | Maximum retry attempts (0 = never retry) |
BackoffRate | number | 2.0 | Multiplier for retry interval (must be ≥ 1.0) |
MaxDelaySeconds | integer | — | Cap on retry interval |
JitterStrategy | string | — | Jitter strategy (e.g., "FULL") |
Rules:
States.ALL must appear alone in its ErrorEquals array.States.ALL must be in the last retrier.MaxAttempts: 0 means “never retry this error.”The Catch field is an array of Catcher objects. After retries are exhausted (or if no retrier matches), the interpreter scans catchers in order.
| Field | Type | Description |
|---|---|---|
ErrorEquals | string[] | Required. Error names to match |
Next | string | Required. State to transition to |
Output | any | Optional. Transform the error output |
Assign | object | Optional. Assign variables from error context |
When a state fails and matches a Catcher, $states.errorOutput is a JSON object with:
Error (string) — the error nameCause (string) — human-readable error descriptionIn a Catch block, Assign and Output can reference:
$states.input — the original state input$states.errorOutput — the error details$states.context — execution contextIf a Catcher matches, the state’s top-level Assign is NOT evaluated — only the Catcher’s Assign runs. If no Output is provided in the Catcher, the state output is the raw Error Output object.
When both Retry and Catch are present, retries are attempted first. Only if retries are exhausted does the Catch apply.
JSONata expressions can fail at runtime. Common causes:
{% $x + $y %} where $x or $y is not a number"TimeoutSeconds": "{% $name %}" where $name is a stringTimeoutSeconds{% $data.nonExistentField %} — JSON cannot represent undefinedPrevent these errors with defensive expressions: use $exists() before accessing fields evaluated at runtime, $type() before arithmetic, and guard filtered results that may return a single object instead of an array. Always guard with $exists() — if a variable was never assigned (e.g., the Catch didn’t fire for that path), referencing it directly throws States.QueryEvaluationError. See transforming-data.md for defensive JSONata examples.
If any branch fails, the entire Parallel state fails. Use States.BranchFailed in Retry/Catch at the Parallel state level.
Individual iteration failures can be tolerated with ToleratedFailurePercentage or ToleratedFailureCount. If the threshold is exceeded, the Map state throws States.ExceedToleratedFailureThreshold.
Retries transient errors with backoff, then catches all errors into a variable and transitions to a Fail state with a descriptive Cause. Guard variable references with $exists() in case the Catch path wasn’t taken.
"ChargePayment": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke",
"Arguments": {
"FunctionName": "arn:aws:lambda:us-east-1:123456789012:function:ChargeCard:$LATEST",
"Payload": "{% $states.input %}"
},
"Retry": [
{
"ErrorEquals": ["ThrottlingException", "ServiceUnavailable"],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2.0,
"JitterStrategy": "FULL"
},
{
"ErrorEquals": ["States.QueryEvaluationError"],
"MaxAttempts": 0
}
],
"Catch": [
{
"ErrorEquals": ["States.ALL"],
"Assign": {
"error": "{% $states.errorOutput %}"
},
"Next": "PaymentFailed"
}
],
"Output": "{% $states.result.Payload %}",
"Next": "ConfirmOrder"
},
"PaymentFailed": {
"Type": "Fail",
"Error": "PaymentError",
"Cause": "{% 'Payment failed for order ' & ($exists($orderId) ? $orderId : 'unknown') & ': ' & ($exists($error.Error) ? $error.Error : 'Unknown') & ' - ' & ($exists($error.Cause) ? $error.Cause : 'No details') & '. Timestamp: ' & $now() %}"
}