

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 处理 Step Functions 工作流程中的错误
<a name="concepts-error-handling"></a>

除 `Pass` 和 `Wait` 状态之外的所有状态都可能遇到运行时错误。有多种原因可能导致错误发生，包括以下所示原因：
+ **状态机定义问题** - 例如没有匹配规则的 Choice 状态
+ **任务失败** - 例如 AWS Lambda 函数中出现异常
+ **暂时性问题** - 例如，网络分区事件

当某个状态报告错误时，Step Functions 默认会使**整个**状态机执行失败。Step Functions 还具有更高级的错误处理功能。您可以将状态机设置为捕获错误、重试失败的状态并优雅地实施错误处理协议。

Step Functions 捕获器可用于 **Task**、**Parallel** 和 **Map** 状态，但不适用于顶层状态机执行失败。要处理您预计可能失败的执行，您的调用方可以处理错误，或者您可以将这些执行嵌套在子工作流中，以捕捉父工作流中的错误。或者，您可以选择使用 EventBridge 总线监听标准工作流程中的`TIMED_OUT`事件，然后调用操作来处理失败的执行。

**处理错误的实际示例**  
要部署包含错误处理的工作流程示例，请参阅本指南中的[在 Step Functions 状态机中处理错误条件](tutorial-handling-error-conditions.md)教程和* AWS Step Functions 研讨会中的*[错误处理](https://catalog.workshops.aws/stepfunctions/handling-errors)。

## 错误名称
<a name="error-handling-error-representation"></a>

Step Functions 使用称为*错误名称*且区分大小写的字符串来标识错误。Amazon States Language 定义了一组内置字符串用于命名广为人知的错误，均以 `States.` 前缀开头。

状态可以报告具有其他名称的错误。但是，这些错误名称不能以 `States.` 前缀开头。

确保您的生产代码可以处理 AWS Lambda 服务异常（`Lambda.ServiceException`和`Lambda.SdkClientException`）。有关更多信息，请参阅*最佳实践*中的“如何[处理短暂的 Lambda 服务异常](sfn-best-practices.md#bp-lambda-serviceexception)”。

** `States.ALL` **  
一个与任何已知错误名称匹配的通配符。  
`States.ALL` 错误类型必须在 `Catcher` 中单独出现，并且无法捕获 `States.DataLimitExceeded` 终端错误或 `Runtime` 错误类型。  
有关更多信息，请参阅[`States.DataLimitExceeded`](#error-data-limit-exceed)和[`States.Runtime`](#states-runtime-error)。

** `States.DataLimitExceeded` **  
无法按 `States.ALL` 错误类型捕获的终端错误。  
由于以下情况而报告：  
+ 连接器的输出大于有效载荷大小配额。
+ 状态的输出大于有效载荷大小配额。
+ `Parameters` 处理后，状态的输入大于有效载荷大小配额。
有关配额的更多信息，请参阅[Step Functions 服务配额](service-quotas.md)。

**`States.ExceedToleratedFailureThreshold`**  
`Map`状 态失败是因为失败项目的数量超过了状态机定义中指定的阈值。有关更多信息，请参阅 [在 Step Functions 中为分布式 Map 状态设置故障阈值](state-map-distributed.md#maprun-fail-threshold)。

** `States.HeartbeatTimeout` **  
`Task` 状态未能发送检测信号的时间超过 `HeartbeatSeconds` 值。  
`HeartbeatTimeout` 出现在 `Catch` 和 `Retry` 字段中。

** `States.Http.Socket` **  
如果 HTTP 任务在 60 秒后超时，就会发生此错误。请参阅[与 HTTP 任务相关的配额](service-quotas.md#service-limits-http-task)。

**`States.ItemReaderFailed`**  
`Map` 状态因无法读取 `ItemReader` 字段中指定的项目来源而失败。有关更多信息，请参阅 `ItemReader （地图）`。

** `States.Permissions` **  
`Task` 状态因没有足够的权限运行指定代码而失败。

**`States.ResultWriterFailed`**  
`Map` 状态因其无法将结果写入 `ResultWriter` 字段中指定的目的地而失败。有关更多信息，请参阅 `ResultWriter （地图）`。

**`States.Runtime`**  
执行因某些无法处理的异常而失败。这些通常是由运行时的错误引起的，例如尝试在 null JSON 有效负载上应用 `InputPath` 或 `OutputPath`。`States.Runtime` 错误不可检索，并且始终会导致执行失败。在 `States.ALL` 上重试或捕获不会捕获 `States.Runtime` 错误。

** `States.TaskFailed` **  
一个在执行期间失败的 `Task` 状态。在 retry 或 catch 中使用时，`States.TaskFailed` 充当通配符，可匹配除 `States.Timeout` 之外的任何已知错误名称。

** `States.Timeout` **  
  
当 `Task` 状态运行时间长度超过 `TimeoutSeconds` 值，或在超过 `HeartbeatSeconds` 值的时段长度内未能发送检测信号时报告。  
如果*嵌套状态机*抛出 `States.Timeout`，则父状态机将收到 `States.TaskedFailed` 错误。  
当整个状态机执行的运行时间超过指定的 `TimeoutSeconds` 值时，也会报告 `States.Timeout` 错误。

**注意**  
Lambda 运行时中未处理的错误历来仅报告为 `Lambda.Unknown`。在较新的运行时中，超时在错误输出中报告为 `Sandbox.Timedout`。  
当 Lambda 超过最大调用次数时，报告的错误将是 `Lambda.TooManyRequestsException`。  
请匹配 `Lambda.Unknown`、`Sandbox.Timedout` 和 `States.TaskFailed` 这几种错误类型，以处理可能出现的错误。您也可以使用 `States.ALL`，但必须单独使用，并且位于列表的末尾。  
有关 Lambda `Handled` 和 `Unhandled` 错误的更多信息，请参阅 [AWS Lambda 开发人员指南](https://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html#API_Invoke_ResponseSyntax)中的 `FunctionError`。

## 出错后重试
<a name="error-handling-retrying-after-an-error"></a>

`Task`、`Parallel` 和 `Map` 状态可以有名为 `Retry` 的字段，其值必须是称为*重试器* 的对象数组。单个重试器表示特定的重试次数，通常是以递增的时间间隔。

当其中一个状态报告错误并且有一个 `Retry` 字段时，Step Functions 会按照数组中列出的顺序扫描重试器。当错误名称出现在某个重试器的 `ErrorEquals` 字段中时，状态机就会按照 `Retry` 字段中的定义进行重试。

如果redriven执行重新运行了已定义[重试](#error-handling-retrying-after-an-error)的 [Task 工作流程状态](state-task.md)、[Parallel 工作流程状态](state-parallel.md) 或[内联 Map 状态](state-map-inline.md)，这些状态的重试尝试计数将重置为 0，以便允许redrive上的最大尝试次数。对于redriven执行，您可以使用控制台跟踪这些状态的单个重试尝试。有关更多信息，请参阅[在 Step Functions 中使用redrive来重新启动状态机执行](redrive-executions.md)中的[redriven执行的重试行为](redrive-executions.md#redrive-retry-behavior)。

重试器包含以下字段：

** `ErrorEquals`（必填）**  
一个匹配错误名称的非空字符串数组。当状态报告错误时，Step Functions 会全面扫描重试器。当错误名称出现在此数组中时，它实施该重试器描述的重试策略。

** `IntervalSeconds`（可选）**  
一个正整数，表示第一次重试尝试之前等待的秒数（默认值为 `1`）。`IntervalSeconds` 的最大值为 99999999。

** `MaxAttempts`（可选）**  
一个正整数，表示重试的最大次数 (默认值为 `3`)。如果错误重复发生超过指定次数，则停止重试并恢复正常错误处理。值为 `0` 表示永不重试错误。 `MaxAttempts` 的最大值为 99999999。

** `BackoffRate`（可选）**  
每次重试后，`IntervalSeconds` 所表示的重试间隔增加的倍数。默认情况下，`BackoffRate` 值会增加 `2.0`。  
例如，假设 `IntervalSeconds` 为 3，`MaxAttempts` 为 3，`BackoffRate` 为 2。第一次重试尝试在错误发生三秒后进行。第二次重试尝试在第一次重试尝试六秒后进行。而第三次重试尝试在第二次重试尝试 12 秒后进行。

** `MaxDelaySeconds`（可选） **  
一个正整数，用于设置重试间隔可增加的最大值（以秒为单位）。该字段与 `BackoffRate` 字段配合使用会很有帮助。在此字段中指定的值将限制应用于每次连续重试尝试的回退率倍数所产生的指数等待时间。您必须为 `MaxDelaySeconds` 指定一个大于 0 小于 31622401 的值。  
如果您未指定此值，则 Step Functions 不会限制两次重试尝试之间的等待时间。

** `JitterStrategy`（可选） **  
一个字符串，用于确定是否在连续重试尝试之间的等待时间中包含抖动。抖动会将同时重试尝试的次数分散到一个随机的延迟时间间隔内，从而减少重试次数。此字符串可接受 `FULL` 或 `NONE` 作为其值。默认值为 `NONE`。  
例如，您将 `MaxAttempts` 设置为 3，`IntervalSeconds` 设置为 2，`BackoffRate` 设置为 2。第一次重试尝试在错误发生两秒后进行。第二次重试在第一次重试尝试后四秒进行，第三次重试在第二次重试尝试后八秒进行。如果将 `JitterStrategy` 设置为 `FULL`，则第一次重试间隔在 0 至 2 秒之间随机进行，第二次重试间隔在 0 至 4 秒之间随机进行，第三次重试间隔在 0 至 8 秒之间随机进行。

**注意**  
重试被视为一种状态转换。有关状态转换如何影响计费的信息，请参阅 [Step Functions 定价](https://aws.amazon.com/step-functions/pricing/)。

### 重试字段示例
<a name="retry-field-examples"></a>

本节包含以下 `Retry` 字段示例。
+ [Retry with BackoffRate](#retrybackoffrate)
+ [Retry with MaxDelaySeconds](#retrymaxdelayseconds)
+ [Retry all errors except States.Timeout](#retrytimeout)
+ [Complex retry scenario](#complexretryeg)

**示例 1-使用重试 BackoffRate**  
下面的 `Retry` 示例进行了两次重试，第一次重试在等待 3 秒后进行。根据指定的 `BackoffRate`，Step Functions 会增加每次重试的间隔时间，直到达到重试尝试的最大次数。在下面的示例中，第二次重试在第一次重试 3 秒后开始。

```
"Retry": [ {
   "ErrorEquals": [ "States.Timeout" ],
   "IntervalSeconds": 3,
   "MaxAttempts": 2,
   "BackoffRate": 1
} ]
```

**示例 2-使用重试 MaxDelaySeconds**  
以下示例进行了三次重试尝试，并将 `BackoffRate` 产生的等待时间限制为 5 秒。第一次重试在等待 3 秒钟后进行。由于 `MaxDelaySeconds` 设置了最长等待时间限制，第二次和第三次重试将在前一次重试后等待 5 秒后进行。

```
"Retry": [ {
    "ErrorEquals": [ "States.Timeout" ],
    "IntervalSeconds": 3,
    "MaxAttempts": 3,
    "BackoffRate":2,
    "MaxDelaySeconds": 5,
    "JitterStrategy": "FULL"
} ]
```

如果未设置 `MaxDelaySeconds`，第二次重试将在第一次重试后 6 秒进行，第三次重试将在第二次重试后 12 秒进行。

**示例 3 – 重试除 States.Timeout 之外的所有错误**  
重试器的 `ErrorEquals` 字段中显示的保留名称 `States.ALL` 是一个通配符，与所有错误名称匹配。它必须单独显示在 `ErrorEquals` 数组中，并且必须显示在 `Retry` 数组的最后一个重试器中。名称 `States.TaskFailed` 也是一个通配符，可匹配除 `States.Timeout` 以外的任何错误。

下面的 `Retry` 字段示例重试了除 `States.Timeout` 以外的任何错误。

```
"Retry": [ {
   "ErrorEquals": [ "States.Timeout" ],
   "MaxAttempts": 0
}, {
   "ErrorEquals": [ "States.ALL" ]
} ]
```

**示例 4 – 复杂的重试场景**  
在单状态执行的上下文中，重试器的参数应用到对该重试器的所有访问。

考虑以下 `Task` 状态。

```
"X": {
   "Type": "Task",
   "Resource": "arn:aws:states:region:123456789012:task:X",
   "Next": "Y",
   "Retry": [ {
      "ErrorEquals": [ "ErrorA", "ErrorB" ],
      "IntervalSeconds": 1,
      "BackoffRate": 2.0,
      "MaxAttempts": 2
   }, {
      "ErrorEquals": [ "ErrorC" ],
      "IntervalSeconds": 5
   } ],
   "Catch": [ {
      "ErrorEquals": [ "States.ALL" ],
      "Next": "Z"
   } ]
}
```

此任务连续失败四次，输出以下错误名称：`ErrorA`、`ErrorB`、`ErrorC` 和 `ErrorB`。出现以下结果：
+ 前两个错误与第一个重试器匹配，导致等待 1 秒和 2 秒。
+ 第三个错误与第二个重试器匹配，导致等待 5 秒。
+ 第四个错误也与第一个重试器匹配。但是，针对该特定错误的两次重试 (`MaxAttempts`) 已达到最大值。因此，该重试器失败，执行会通过 `Catch` 字段将工作流重定向到 `Z` 状态。

## 回退状态
<a name="error-handling-fallback-states"></a>

`Task`、`Map` 和 `Parallel` 状态可以具有名为 `Catch` 的字段。此字段的值必须是称为*捕获器* 的对象的数组。

捕获器包含以下字段。

** `ErrorEquals`（必填）**  
一个与错误名称匹配的非空字符串数组，由相同名称的重试器字段完全按其原样指定。

** `Next`（必填）**  
一个字符串，必须与状态机的状态名称之一完全匹配。

** `ResultPath`（JSONPath，可选）**  
一个[路径](concepts-input-output-filtering.md)，确定捕获器将什么输入发送到在 `Next` 字段中指定的状态。

当某个状态报告错误并且没有 `Retry` 字段或者重试无法解决错误时，Step Functions 按照数组中列出的顺序全面地扫描捕获器。当错误名称显示在捕获器的 `ErrorEquals` 字段中时，状态机转换为在 `Next` 字段中指定的状态。

捕获器的 `ErrorEquals` 字段中显示的保留名称 `States.ALL` 是一个通配符，与所有错误名称匹配。它必须单独显示在 `ErrorEquals` 数组中，并且必须显示在 `Catch` 数组的最后一个捕获器中。名称 `States.TaskFailed` 也是一个通配符，可匹配除 `States.Timeout` 以外的任何错误。

以下示例说明的是一个 `Catch` 字段，该字段在 Lambda 函数输出未处理的 Java 异常时转换为名为 `RecoveryState` 的状态。否则，该字段转换为 `EndState` 状态。

```
"Catch": [ {
   "ErrorEquals": [ "java.lang.Exception" ],
   "ResultPath": "$.error-info",
   "Next": "RecoveryState"
}, {
   "ErrorEquals": [ "States.ALL" ],
   "Next": "EndState"
} ]
```

**捕获器能捕获多少个错误？**  
每个捕获器可以指定**多个要处理的错误**。

### 错误输出
<a name="error-handling-error-output"></a>

Step Functions 转换为 Catch 名称中指定的状态时，对象通常包含字段 `Cause`。此字段的值是人类可读格式的错误说明。该对象称为*错误输出*。

在前面的 JSONPath 示例中，第一个捕手包含一个`ResultPath`字段。其工作方式类似于状态顶级中的 `ResultPath` 字段，有两种可能的结果：
+ 获取该状态执行的结果，并覆盖状态的全部或部分输入。
+ 获取结果并将其添加到输入中。在由捕获器处理错误时，状态执行的结果是错误输出。

因此，在本示例中，对于第一个捕获器，如果输入中还没有名为 `error-info` 的字段，捕获器就会将错误输出添加到输入中。然后，捕获器会将整个输入发送到 `RecoveryState`。对于第二个捕获器，错误输出会覆盖输入，捕获器只会将错误输出发送到 `EndState`。

对于 JSONPath 工作流程，如果您不指定该`ResultPath`字段，则该字段默认为`$`，它会选择并覆盖整个输入。

当状态同时包含 `Retry` 和 `Catch` 字段时，Step Functions 会首先使用任何合适的重试器。如果重试策略无法解决错误，Step Functions 会应用匹配的捕获器转换。

### 原因有效负载和服务集成
<a name="error-handling-integrations-json"></a>

捕获器会返回一个字符串有效负载作为输出。在使用诸如 Amazon AWS CodeBuild Athena 或之类的服务集成时，您可能需要将字符串`Cause`转换为 JSON。下面这个带有内置函数的 `Pass` 状态示例展示了如何将 `Cause` 字符串转换为 JSON。

```
"Handle escaped JSON with JSONtoString": {
  "Type": "Pass",
  "Parameters": {
    "Cause.$": "States.StringToJson($.Cause)"
  },
  "Next": "Pass State with Pass Processing"
},
```

## 使用 Retry 和 Catch 的状态机示例
<a name="error-handling-examples"></a>

以下示例中定义的状态机假定存在两个 Lambda 函数：一个始终失败，另一个等待足够长的时间，以允许发生状态机中定义的超时。

这是始终失败的 Lambda 函数的定义，返回消息 `error`。在后面的状态机示例中，此 Lambda 函数名为 `FailFunction`。有关创建 Lambda 函数的信息，请参阅[第 1 步：创建 Lambda 函数](tutorial-creating-lambda-state-machine.md#create-lambda-function)部分。

```
exports.handler = (event, context, callback) => {
    callback("error");
};
```

这是一个休眠 10 秒的 Lambda 函数的定义。在后面的状态机示例中，此 Lambda 函数名为 `sleep10`。

```
exports.handler = (event, context, callback) => {
    setTimeout(function(){
    }, 11000);
};
```

**函数的超时设置**  
在为示例创建 Lambda 函数时，请记住将高级设置中的 `Timeout` 值设置为 11 秒。

### 使用 Retry 处理失败
<a name="error-handling-handling-failure-using-retry"></a>

此状态机使用 `Retry` 字段重试失败的函数，并输出错误名称 `HandledError`。该函数重试两次，在两次重试之间使用指数回退。

```
{
   "Comment": "A Hello World example invoking Lambda function",
   "StartAt": "HelloWorld",
   "States": {
      "HelloWorld": {
         "Type": "Task",
         "Resource": "arn:aws:lambda:region:123456789012:function:FailFunction",
         "Retry": [ {
            "ErrorEquals": ["HandledError"],
            "IntervalSeconds": 1,
            "MaxAttempts": 2,
            "BackoffRate": 2.0
         } ],
      "End": true
      }
   }
}
```

此变体使用预定义的错误代码 `States.TaskFailed`，这与 Lambda 函数输出的任意错误匹配。

```
{
   "Comment": "Hello World example which invokes a AWS Lambda function",
   "StartAt": "HelloWorld",
   "States": {
      "HelloWorld": {
         "Type": "Task",
         "Resource": "arn:aws:lambda:region:123456789012:function:FailFunction",
         "Retry": [ {
            "ErrorEquals": ["States.TaskFailed"],
            "IntervalSeconds": 1,
            "MaxAttempts": 2,
            "BackoffRate": 2.0
         } ],
         "End": true
      }
   }
}
```

**处理 Lambda 异常的最佳实践**  
引用 Lambda 函数的任务应处理 Lambda 服务异常。有关更多信息，请参阅最佳实践中的[处理短暂的 Lambda 服务异常](sfn-best-practices.md#bp-lambda-serviceexception)。

### 使用 Catch 处理失败
<a name="error-handling-handling-failure-using-catch"></a>

此示例使用 `Catch` 字段。当 Lambda 函数输出错误时，将会捕获错误，并且状态机转换为 `fallback` 状态。

```
{
   "Comment": "Hello World example which invokes a AWS Lambda function",
   "StartAt": "HelloWorld",
   "States": {
      "HelloWorld": {
         "Type": "Task",
         "Resource": "arn:aws:lambda:region:123456789012:function:FailFunction",
         "Catch": [ {
            "ErrorEquals": ["HandledError"],
            "Next": "fallback"
         } ],
         "End": true
      },
      "fallback": {
         "Type": "Pass",
         "Result": "Hello, AWS Step Functions!",
         "End": true
      }
   }
}
```

此变体使用预定义的错误代码 `States.TaskFailed`，这与 Lambda 函数输出的任意错误匹配。

```
{
   "Comment": "Hello World example which invokes a AWS Lambda function",
   "StartAt": "HelloWorld",
   "States": {
      "HelloWorld": {
         "Type": "Task",
         "Resource": "arn:aws:lambda:region:123456789012:function:FailFunction",
         "Catch": [ {
            "ErrorEquals": ["States.TaskFailed"],
            "Next": "fallback"
         } ],
      "End": true
      },
      "fallback": {
         "Type": "Pass",
         "Result": "Hello, AWS Step Functions!",
         "End": true
      }
   }
}
```

### 使用 Retry 处理超时
<a name="error-handling-handling-timeout-using-retry"></a>

此状态机根据在 `TimeoutSeconds` 中指定的超时值，使用 `Retry` 字段重试超时的 `Task` 状态。Step Functions 在此 `Task` 状态下重试两次 Lambda 函数调用，两次重试之间会出现指数回退。

```
{
   "Comment": "Hello World example which invokes a AWS Lambda function",
   "StartAt": "HelloWorld",
   "States": {
      "HelloWorld": {
         "Type": "Task",
         "Resource": "arn:aws:lambda:region:123456789012:function:sleep10",
         "TimeoutSeconds": 2,
         "Retry": [ {
            "ErrorEquals": ["States.Timeout"],
            "IntervalSeconds": 1,
            "MaxAttempts": 2,
            "BackoffRate": 2.0
         } ],
         "End": true
      }
   }
}
```

### 使用 Catch 处理超时
<a name="error-handling-handling-timeout-using-catch"></a>

此示例使用 `Catch` 字段。在出现超时的情况下，状态机会转换为 `fallback` 状态。

```
{
   "Comment": "Hello World example which invokes a AWS Lambda function",
   "StartAt": "HelloWorld",
   "States": {
      "HelloWorld": {
         "Type": "Task",
         "Resource": "arn:aws:lambda:region:123456789012:function:sleep10",
         "TimeoutSeconds": 2,
         "Catch": [ {
            "ErrorEquals": ["States.Timeout"],
            "Next": "fallback"
         } ],
         "End": true
      },
      "fallback": {
         "Type": "Pass",
         "Result": "Hello, AWS Step Functions!",
         "End": true
      }
   }
}
```

**保留状态输入和错误 JSONPath**  
在中 JSONPath，您可以使用保留状态输入和错误`ResultPath`。请参阅[ResultPath 用于在 a 中同时包含错误和输入 `Catch`](input-output-resultpath.md#input-output-resultpath-catch)。