Error handling in SonataFlow

This document describes how you can handle the errors that might occur in SonataFlow.

The Serverless Workflow specification provides an error handling mechanism, enabling you to handle the errors that might happen during the interactions between the workflow and external systems.

When an error occurs, it changes the regular workflow sequence. In such cases, a workflow state transitions to an alternative state that can potentially handle the error, instead of transitioning to the predefined state.

Note that error definition for a workflow is optional. If error handling is not defined, then the workflow execution is aborted when an error occurs. As a developer, you can consider the error handling in SonataFlow as a try-catch or a goto construct.

Error definition

An error definition in a workflow is composed of a name and code parameters. The name is a short and natural language description of an error, such as wrong parameter. The code parameter helps the implementation to identify the error.

The code parameter is mandatory and the engine uses different strategies to map the provided value to an exception encountered at runtime. The available strategies include fully qualified class name, error message, and status code.

Fully Qualified Class Name (FQCN)

You can define the mapping as the fully qualified Java class name of an exception type.

To determine if an exception is handled by an error definition, a workflow first checks whether or not the type of the Java exception is an instance of the exception that is specified in the error definition. Otherwise, the workflow recursively applies the same check to any embedded exception within the original Java one, till the exception chain is exhausted.

Table 1. Runtime exception examples:
Runtime exception Error code definition

java.lang.RuntimeException

java.lang.RuntimeException

java.lang.IllegalStateException

java.lang.RuntimeException

Error message

You can define the mapping as a partial match of the error message using regex patterns.

A workflow verifies whether the message of the Java exception matches the value provided in the error code. The validation is performed using a regex matcher. The validation is successful if a partial match is found. Note that the validation is performed in a case-sensitive manner. If case-insensitive pattern matching is desired, use the embedded flag expression (?i) within the regular expression.

Table 2. Error message examples:
Error message of runtime exception Error code definition

Unknown error, status code 400

Unknown error

Unknown error, status code 400

(?i)Error or error

Unknown error, status code 400

(.*)status code 4[0-9]{2} or status code 4[0-9]{2}

Status code

You can define the mapping as a status code returned by the invoked service.

The external services return status codes in the event of a failure. For example, HTTP status codes are returned by REST or OpenAPI service invocations. In this case, the workflow verifies the defined error code against the HTTP code from the underlying response object.

You can define the error code either as an HTTP code or as a colon-separated string, in which the error definition contains the error code in the last part.

Table 3. Status code examples:
HTTP status code Error code definition

400

400

400

HTTP:400

Example of error handling

The workflow in the serverless-workflow-error-quarkus example application illustrate the usage of the error handling mechanism.

error handling
Figure 1. Example of error handling

The workflow accepts an integer number as the input model. When the workflow starts, it invokes the isEven Java service, which accepts that integer number as a parameter. This service throws an IllegalArgumentException exception if the integer number is odd. This means that if the integer number is even, the workflow transitions to the even state, however, if the integer number is odd, the workflow transitions to the odd state, resulting in a different model output.

Example checkEven state
{
      "name": "checkEven",
      "type": "operation",
      "actions": [
        {
          "name": "checkEvenAction",
          "functionRef": {
            "refName": "isEven",
            "arguments": {
              "number": "$.number"
            }
          }
        }
      ],
      "transition": "even",
      "onErrors": [
        {
          "errorRef": "odd number",
          "transition": "odd"
        }
      ]
    }

The workflow defines a function that invokes the isEven method of org.kie.kogito.examples.EvenService class. This function uses a custom function type, enabling SonataFlow to invoke Java methods.

Example isEven function definition
{
    "name": "isEven",
    "type": "custom",
    "operation": "service:java:org.kie.kogito.examples.EvenService::isEven"
}

To handle the error, the workflow defines an odd number error which handles any runtime exception. As the IllegalArgumentException is a child of RuntimeException, if isEven method throws the exception, it will be handled.

Example error definition
"errors": [
    {
      "name": "odd number",
      "code": "java.lang.RuntimeException"
     }
  ]

The Inject state is used to populate the model with specific JSON payload. Therefore, the even and odd state defines the message property as even and odd respectively.

   {
      "name": "even",
      "type": "inject",
      "data": {
        "numberType": "even"
      },
      "transition": "finish"
    },
    {
      "name": "odd",
      "type": "inject",
      "data": {
        "numberType": "odd"
      },
      "transition": "finish"
    }

The finish state in the serverless-workflow-error-quarkus example application displays the model content to the console, so you can verify that the expected message has been set.

    {
      "name": "finish",
      "type": "operation",
      "actions": [
        {
          "name": "printAction",
          "functionRef": {
            "refName": "printMessage",
            "arguments": {
              "message": "$.numberType"
            }
          }
        }
      ],
      "end": {
        "terminate": "true"
      }
    }

Found an issue?

If you find an issue or any misleading information, please feel free to report it here. We really appreciate it!