AWS Step Functions自定义状态出现不可达状态与Schema验证失败问题排查
报错信息
Resource handler returned message: "Invalid State Machine Definition: 'SCHEMA_VALIDATION_FAILED: Expected value of type [ARRAY] at /States/ProcessFile/Catch, MISSING_TRANSITION_TARGET: State "TerminalState" is not reachable. at /States/ProcessFile/ItemProcessor/States/TerminalState' (Service: AWSStepFunctions; Status Code: 400; Error Code: InvalidDefinition; Request ID: cdbda38e-12d5-4dee-81a3-020a2a00cd32; Proxy: null)" (RequestToken: 81bdef9c-408a-d6b2-af56-e1f42eb54c50, HandlerErrorCode: InvalidRequest)
用户代码(自定义Map状态)
IDictionary<string, object> stateJson = new Dictionary<string, object> { { "Type", "Map" }, { "ItemProcessor", new Dictionary<string, object> { { "ProcessorConfig", new Dictionary<string, object> { { "Mode", "DISTRIBUTED" }, { "ExecutionType", "EXPRESS" } } }, { "StartAt", "AudienceListUserValidation" }, { "States", new Dictionary<string, object> { { "AudienceListUserValidation", new Dictionary<string, object> { { "Type", "Task" }, { "Resource", "arn:aws:states:::lambda:invoke" }, { "Parameters", new Dictionary<string, object> { { "FunctionName", userValidationfunction.Function.FunctionName }, { "Payload.$", "$" } } }, { "ResultPath", JsonPath.DISCARD }, { "Catch", catchSerialized }, { "End", true }, } }, { "TerminalState", new Dictionary<string, object> { { "Type", "Task"}, { "Resource", "arn:aws:states:::lambda:invoke"}, { "Parameters", new Dictionary<string, object> { { "FunctionName", errorFunction.Function.FunctionName }, { "Payload.$", "$" } } }, { "End", true } } } } } } }, { "Label", "ProcessFile" }, { "MaxConcurrency", 5 }, { "ItemReader", new Dictionary<string, object> { { "Resource", "arn:aws:states:::s3:getObject" }, { "ReaderConfig", new Dictionary<string, object> { { "InputType", "CSV" }, { "CSVHeaderLocation", "FIRST_ROW" } } }, { "Parameters", new Dictionary<string, object> { { "Bucket.$", "$.S3BucketName" }, { "Key.$", "$.S3FileName" } } } } }, { "ItemSelector", new Dictionary<string, object> { { "MatchingFieldId.$", "$$.Map.Item.Value.Email"}, { "FileUploadId.$", "$.FileUploadId"} } }, { "ResultPath", JsonPath.DISCARD } };
catchSerialized定义
var catchSerialized = JsonConvert.SerializeObject(new[] { new { ErrorEquals = new[] { "States.ALL" }, Next = "TerminalState" } });
用户疑问
原本期望AudienceListUserValidation执行失败时跳转至TerminalState,但出现上述报错。另外使用CDK,是否可以用OnFailure属性替代Catch?示例代码:
audienceListUserValidationFuncProps.OnFailure = new LambdaDestination(errorFunction.Function, new LambdaDestinationOptions { ResponseOnly = true, });
问题原因与修复
1. Catch字段类型错误
你将catchSerialized序列化成了JSON字符串,但Step Functions状态定义中Catch字段要求的是数组对象,而非字符串。当前代码把字符串赋值给Catch,直接触发Schema验证失败(提示Expected value of type [ARRAY])。
2. TerminalState不可达(关联错误)
因为Catch字段类型错误,Step Functions无法识别AudienceListUserValidation的错误跳转逻辑,进而判定TerminalState未被任何状态引用,导致MISSING_TRANSITION_TARGET错误。
修复代码
将catchSerialized从JSON字符串改为对象数组,无需序列化,直接用字典结构定义:
var catchConfig = new List<Dictionary<string, object>> { new Dictionary<string, object> { { "ErrorEquals", new[] { "States.ALL" } }, { "Next", "TerminalState" } } };
然后替换AudienceListUserValidation状态中的Catch配置:
{ "Catch", catchConfig },
CDK中OnFailure与Catch的区别
不能直接用Lambda的OnFailure替代Step Functions的Catch:
- Lambda的
OnFailure是Lambda函数层面的错误处理,仅当Lambda执行触发崩溃、超时等特定错误时,才会调用指定目标服务,属于函数自身配置。 - Step Functions的
Catch是工作流层面的错误处理,不仅能捕获Lambda执行错误,还能处理Step Functions状态本身的错误(如参数解析、状态跳转错误等),且能控制工作流后续走向(跳转其他状态、终止等)。
如果用CDK构建状态机,建议使用官方Step Functions构造库(aws-cdk-lib/aws-stepfunctions),更优雅地定义错误逻辑:
var validationTask = new LambdaInvoke(this, "AudienceListUserValidation", new LambdaInvokeProps { LambdaFunction = userValidationfunction.Function, Payload = JsonPath.StringAt("$"), ResultPath = JsonPath.DISCARD }); var terminalTask = new LambdaInvoke(this, "TerminalState", new LambdaInvokeProps { LambdaFunction = errorFunction.Function, Payload = JsonPath.StringAt("$") }); validationTask.AddCatch(terminalTask, new CatchProps { Errors = new[] { "States.ALL" } }); var mapState = new Map(this, "ProcessFile", new MapProps { MaxConcurrency = 5, ItemProcessor = new Processor(validationTask), ItemReader = new S3CsvReader(new S3CsvReaderProps { Bucket = Bucket.FromBucketName(this, "FileBucket", "<your-bucket-name>"), Key = JsonPath.StringAt("$.S3FileName"), HeaderLocation = CsvHeaderLocation.FIRST_ROW }), ItemSelector = new Dictionary<string, object> { { "MatchingFieldId.$", "$$.Map.Item.Value.Email" }, { "FileUploadId.$", "$.FileUploadId" } }, ResultPath = JsonPath.DISCARD });
内容的提问来源于stack exchange,提问作者SomeStudent

