diff --git a/VERSION b/VERSION index cb925bf..b9765d7 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -v0.7.1 -Add team tag to activities \ No newline at end of file +v0.7.2 +Truncate error messages to fit within AWS limits. diff --git a/cmd/sfncli/error_names.go b/cmd/sfncli/error_names.go index 65253e6..b418dfc 100644 --- a/cmd/sfncli/error_names.go +++ b/cmd/sfncli/error_names.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/sfn" @@ -26,11 +27,15 @@ type TaskFailureError interface { func (t TaskRunner) sendTaskFailure(err TaskFailureError) error { t.logger.ErrorD("send-task-failure", logger.M{"name": err.ErrorName(), "cause": err.ErrorCause()}) + // Limits from https://docs.aws.amazon.com/step-functions/latest/apireference/API_SendTaskFailure.html + const maxErrorLength = 256 + const maxCauseLength = 32768 + // don't use SendTaskFailureWithContext, since the failure itself could be from the parent // context being cancelled, but we still want to report to AWS the failure of the task. _, sendErr := t.sfnapi.SendTaskFailure(&sfn.SendTaskFailureInput{ - Error: aws.String(err.ErrorName()), - Cause: aws.String(err.ErrorCause()), + Error: aws.String(truncateString(err.ErrorName(), maxErrorLength, "[truncated]")), + Cause: aws.String(truncateString(err.ErrorCause(), maxCauseLength, "[truncated]")), TaskToken: &t.taskToken, }) if sendErr != nil { @@ -39,6 +44,20 @@ func (t TaskRunner) sendTaskFailure(err TaskFailureError) error { return err } +// Returns its input truncated to maxLength, with the ability to replace the end to indicate truncation. +// +// For example, truncateString(s, l, "") just truncates to length l. But truncateString(s, l, "xy") will +// first truncate to length l, then replace the last two characters with "xy" +func truncateString(s string, maxLength int, truncationIndicatorSuffix string) string { + if len(s) <= maxLength { + return s + } + // when we cut out some number of bytes from the end, we may be cutting in the middle of a multi-byte unicode char + // if so, we can use ToValidUTF8 to trim it a teeny bit further to eliminate the whole char. + // (Note, this does mean invalid UTF8 inputs will see more changes than expected, but we won't worry about that) + return strings.ToValidUTF8(s[:maxLength-len(truncationIndicatorSuffix)], "") + truncationIndicatorSuffix +} + // TaskFailureUnknown is used for any error that is unexpected or not understood completely. type TaskFailureUnknown struct { error