Thoughts on Error Handling

kamalika chaudhuri
Another Integration Blog
8 min readNov 17, 2022

Dear readers,

I hope you are all doing well. While serving customers of various domains, such as banking, retail, pharmaceutical, and automobile industry, I have helped them with their MuleSoft implementation journeys. Today, in this blog, I want to share some of my observations on the different aspects of error handling in MuleSoft that I have seen.

Error Handling Basics

In MuleSoft, when any connector encounters an error, the flow execution stops, an error is thrown, and we can handle the error using one of the two error handlers I will describe below, as required.

  • On-error continue: this will return success 200 and will not throw the error back to source. Any transactions at this point are committed.
On Error Continue
  • On-error propagate: Error is re-thrown back to source. This rolls back any transaction.

We might place this error handler in the following situations:

  • In the flows error handling section:
  • In the try scope’s error handling section:
  • We can define an unattached error handler which can contain one or more on-error continue or on-error propagate handler components. which handles the error. This unattached error handler can be set as the default error handler under global configuration to be applicable throughout the application. It can be referenced from the error handling section of the main flow containing the API kit router.
Unattached Error Handler
Error Handler Reference
Set your custom error handler as default

The next option is the most reusable one; putting all error handling logic in a global-error-handler.xml file is a development best practice.

Once we have caught the error, we can try to retrieve information from it which can be returned as an API response, logged, or used to form an alert message to send as an error alert to a notification API.

We can retrieve the error type from the error object to log the original error by getting:

errorType: (error.errorType.namespace default “MULE”) ++ “:” ++ (error.errorType.identifier default “ANY”)

Next, we can fetch the error description which provides us with more insights about the error:

errorDescription: error.detailedDescription

What happens if we don’t handle errors or don't define error handlers? Errors will be handled by MuleSoft’s default error handler which will log the error and propagate it.

Until now, the basics of error handling are covered.

Let's go a bit deeper and explore the following topics.

Error Propagation

Sometimes, when we are calling a downstream API from within the Mule API code, we might want to propagate the same error payload and error status code that is thrown by the downstream API. To do so, we would require the error payload and the error status code. We can retrieve those in two ways:

First, retrieve from the error object itself:

Set payload to error.errorMessage.payload
Set variable httpStatus to error.errorMessage.attributes.statusCode
propagate when Api returns 400
propagating same error message which is set

I am setting variable httpStatus because I have set up my listener as shown below:

For this demonstration, I have created another flow which listens and returns 400 when an empty payload is passed. This shows that the payload and status code I have set in this flow is returned by our test flow.

Complete flow

This is what the /error endpoint returns:

When the /error endpoint is called from within the /test, as we are retrieving the same error payload and status code and resetting it, we will see the same response returned.

The second option to propagate an error would be to configure the HTTP request connectors success code validator to consider the error status codes as success. In this, the http status and the error payload are available in attributes.statusCode and the payload itself.

StatusCode Range
Individual Status Codes

Error Handling for a Mule API vs Error Handling for Mule Application

Error handling for a Mule API

By Mule API I mean an API that accepts a request and returns and response. In such cases, when we are designing the error handler, we will log the error and set up appropriate responses for different types of errors that might occur in the API. We can do so by adding a common error handler and adding multiple on-error propagates to handle each error, as shown below.

Above is the simplest possible example of a common-error-handler for a Mule API.

It is important that we put the error handler for type ANY last so that it catches all of the error that are not caught by any of the handlers above. Also, if we put the error handler for type ANY at top, then it will catch all errors and we will lose fine grain control of handling the different types of errors.

Error Handling for Mule Application

This means an application that does not immediately return a response. For example, an application that is triggered by a scheduler, queue, or a listener of a connector which does not return an immediate response (for example, an IDoc listener). These kind of applications are designed to do batch or sync jobs.

Now, I will describe how to design an error handler for such Mule applications. We will start by designing an error handler that will log the error, then we need to put logic to implement corrective actions for known error scenarios. For example, updating a row in Salesforce or the database with the error flag against the record. We can separate retriable and nonretriable errors and add logic to retry, if applicable. Retriable errors can result from connectivity issues which might be fixed on retry. An example of a nonretriable error can result from a bad request or faulty data. Corrective actions for nonretriableable errors can be flagging in the record, sending an alert to the respective team. If the sync job is constructed in a way such that the faulty record is picked up in all cycles, then logic to flag the data should be implemented, so that it is not picked up in the next cycle. For such scenarios, the data is pushed to a dead letter queue or saved in a location such as a database or file location for manual processing.

The error handler snippet shown below is taken from a sync job API which fetches new/updated record details from Salesforce, updates the record details in an external system, and updates the status of the update back in Salesforce by the record’s ID. If at any point the external system update operation encounters a non-retry-able error, then, as part of corrective action, a flag is updated in Salesforce that prevents the error-prone record from being picked up in next cycle and requires a manual intervention.

flag records in salesforce which encounter non retryable error

Custom Errors
MuleSoft provides the option of raising a custom error to identify and handle specific error scenarios. We can broadly classify errors in any Mule application as one of the following:

  • Business errors
  • Technical errors

Business errors are scenarios where a business condition is not satisfied. For example, trying to create a record that already exists. We can put checks and validations in place for such conditions and raise appropriate custom errors for easy error identification in logs and can be handled in the error handler.

Custom errors can be raised in two ways via the raise error component or the error-mapping option in Mule connectors. See the example below where we are raising a custom error if the requested payload is empty.

Setting custom error in error mapping section

We can also do this using the choice and raise error component.

Raise custom error using raise error component

Finally, we can handle the custom error that we just raised.

Handling a custom error

Technical Errors

Technical errors mostly occur in runtime. We generally do not raise custom errors for technical errors. But consider when a legacy system API results in an internal server error (500), we want to handle it in a unique way and send a notification to the team with details for the payload and failure. Then, a custom error can be mapped to the error HTTP: INTERNAL_SERVER_ERROR to APP: INTERNAL_SERVER_ERROR and handler logic which listens for APP: INTERNAL_SERVER_ERROR type of error.

Packaging the Error Handler

Part of the project: error handlers can be part of the project structure. This option is not reusable and mostly used for projects having very few APIs.

Custom Connector: error handling code for an API can be packaged as a connector and used while implementing a project. This option offers flexibility, such us adding an error message. However, a large part of additional or external logic remains in the code.

Mule Plugin: error handlers can be packaged as a Mule plugin and can be plugged in as Maven dependency in any Mule application project. If you are using this option, remember to add an import statement in the global configurations for the error-handler’s xml file. This plugin, if added with options to add extend and override logic by the error-handler code, gives a lot of flexibility for project implementation.

Lastly, to make all reusable error handler diverse, it is essential to add basic error handler code for both Mule APIs (i.e., implemented with API kit router) and Mule applications (i.e., async or batch APIs). Both handlers give options to the implementing project to extend and override.

Thank you for reading this blog. If you like this article, please leave a clap and follow me so you can read more blog like this.

--

--