Often times when you are processing data through a flow, you may want to treat certain errors differently than others. For instance, if you are trying to select records from Salesforce, you would want to handle a record not found error differently than an out of memory error. For this reason, MuleSoft allows us to handle errors based on use cases as well as the types of errors that are being thrown.
Processor level error handling
In Mule 4, a try scope is used when you want to attempt to perform an action and catch the error and attempt to handle the error before failing an entire flow. The basic process for a try scope is:
- Attempt to perform an action.
- If successful, continue on… otherwise, attempt to catch the error.
- If the error is caught, perform some process.
- Based on the design, either allow the flow to continue or fail the flow.
Note that you can put a try scope anywhere within your flow: inside a flow, inside error handler, or even within other try scopes.
For our try-scope examples, we will use a similar flow to that which we used in all of the previous examples:
The basic idea of this flow is that the flow will attempt to assert that the payload is number (which will fail because it is a string) and then attempt to handle any error thrown here with an error handling scope within the try scope.
Try Scope: On Error Continue
- Payload is successfully set to “Success – Started Flow”
- The Is Number validator creates an Error Object because the payload isn’t an integer
- Try scope execution stops
- #[error.description] = “payload is not a valid INTEGER value”
- #[error.errorType] = VALIDATION:INVALID_NUMBER
- The Try Scope’s On Error Continue handles the error
- The payload is set to “Error – Try Scope”
- The mainFlow execution continues
- The payload is set to “Success – Finished Flow”
- “Success – Finished Flow” is returned to the requestor
- HTTP Status Code: 200
Some notes on the above example is that the execution of the “Success – Finished Try Scope” set payload processor will not execute despite the On Error Continue scope because, by design, execution of the current scope (in this case, the try scope) stops as soon as there is an error and immediately goes to the current scope’s error handling scope. With that being said, the final “Success – Finished Flow” set payload processor does execute because the try scope used an on error continue scope which means it passes a success message back to the mainFlow.
Try Scope: On Error Propagate
- Payload is successfully set to “Success – Started Flow”
- The Is Number validator creates an Error Object because the payload isn’t an integer
- The Try Scope execution stops
- #[error.description] = “payload is not a valid INTEGER value”
- #[error.errorType] = VALIDATION:INVALID_NUMBER
- The Try Scope’s On Error Propagate handles the error
- The payload is set to “Error – Try Scope”
- Because there is no flow error handler for the main flow, the default behavior of a flow is to propagate the error
- “payload is not a valid INTEGER value” is returned to the requestor in the body of the HTTP request
- HTTP Status Code: 500
The key difference with this particular example from the previous is that because we used an On Error Propagate scope, an error message is passed back to the mainFlow which causes execution of that flow to stop and propagate the error. Therefore, the final “Success – Finished Flow” set payload processor is never executed whereas, in the first example, it was executed because of the On Error Continue scope.
As if the processor level of error handling wasn’t granular enough, MuleSoft allows you to go one level deeper to handle error by utilizing the ability to handle specific error messages in their own ways.
Handling specific error messages
With any error handling scope in Mule flows, MuleSoft gives us the ability to handle specific errors. This can be very useful if you wish to act on specific errors in specific ways. For instance, if you are trying to insert a record into a database, you would want to handle a duplicate record error differently than you would handle an access issue.
In order for you to understand how to handle specific errors, you have to first understand the basics of what Mule Errors look like.
The first thing to understand is that every error has a type, and every error type is comprised of two separate pieces: a namespace and an identifier. The first portion of the error type (before the colon) is the namespace and the second portion of the error type (after the colon) is called the identifier. Some examples of error types are as follows:
- HTTP:UNAUTHORIZED
- HTTP:CONNECTIVITY
- VALIDATION:INVALID_BOOLEAN
The second piece to understand is that every error type has a parent (i.e. error types are hierarchical). Below is a sampling of popular error types (though not an exhaustive list):
By default, when you bring an error handling scope (either On Error Propagate or On Error Continue) into a flow, it is set up to catch errors of type ANY (the parent of all error types). If you wish to change this and handle specific errors, you can define the type of errors to handle. You can either select the specific error type or provide logic to decide what type of error message you would like to handle.
With that being said, you can (and often do) define several different error handling scopes within a single error handler. When this happens, how do you determine which scope will catch the error?
- If a flow has an error handler
- The error is handled by the first error scope whose condition evaluates to true
- This is done from top to bottom of the code
- If no scope conditions evaluate to true, the error is handled by the Mule Default Error Handler
- Note: Even if it’s defined, the error will NOT be handled by a global error handler in this case
- The error is handled by the first error scope whose condition evaluates to true
- If a flow has no error handling defined at the processor/flow level
- The error will be handled by the applications default error handler. This may be the Mule Default Error Handler, or (if defined) a global error handler
Let’s now see how this is handled in a real example.
In the simple example that we have been using this whole time, note that the error type that is thrown is “VALIDATION:INVALID_NUMER”. This is important to note because we are going to set up multiple error scopes in this flow, but the only one that will match is the second On Error Continue scope which is set to match the VALIDATION:INVALID_NUMER error type.
In the below example, the is number validator will fail throwing a VALIDATION:INVALID_NUMER error type which will be caught by the second error scope and therefore handled.
- Payload is successfully set to “Success – Started Flow”
- The Is Number validator creates an Error Object because the payload isn’t an integer
- The Try Scope execution stops
- #[error.description] = “payload is not a valid INTEGER value”
- #[error.errorType] = VALIDATION:INVALID_NUMBER
- The On Error Continue shape that handles the error
- This is the first error scope that matches the type of “VALIDATION: INVALID_NUMBER”
- The payload is set to “Error – Invalid Number”
- “Error = Invalid Number” is returned to the requestor in the body of the HTTP request
- HTTP Status Code: 200
Note that the error wasn’t caught in the first error scope because the error type is VALIDATION:INVALID_NUMBER and the first error scope is looking for an error of type STREAM_MAXIMUM_SIZE_EXCEEDED and therefore the types didn’t match, so the error went to the next try scope in the code where the error type matched the expected error type.
Now, what happens when we specify the specific error types we want to handle, but the error that is thrown isn’t one that we specified?
- Payload is successfully set to “Success – Started Flow”
- The Is Number validator creates an Error Object because the payload isn’t an integer
- The mainFlow execution stops
- #[error.description] = “payload is not a valid INTEGER value”
- #[error.errorType] = VALIDATION:INVALID_NUMBER
NOTE: The error type of VALIDATION:INVALID_NUMBER is not one of the error types defined in the error scopes defined
Because no error scopes match the given error type, the default behavior of a flow is to propagate the error
- Note – The global error handler isn’t the scope to handle this error
- “payload is not a valid INTEGER value” is returned to the requestor in the body of the HTTP request
- HTTP Status Code: 500
Some important things to note with this example is that the error thrown by the is number validator is not one of errors caught in the error scopes and is therefore handled by the Mule Default Error Handler (not the defined global error handler). Moral of this story, if you want to define how to handle the error message thrown, even if it doesn’t match one of the error types you define, set the last scope in the error handling block to catch the error type of ANY. This will enable you to define what to do if all else fails.
Conclusion
I have found that if you break error handling down to its basic components and remember a few key concepts, you can easily solve any error handling puzzle. The key points to remember are:
- On error continue: RED in, GREEN out
- On error propagate: RED in, RED out
- When an error occurs it is handled in the following order:
- Processor level
- Flow level
- Application level
- If you specify specific errors to be handled, the first one (from top to bottom) to match handles the error
- If the error doesn’t match anything, it is handled by the Mule Default Error Handler
Now that you’re an error handling pro, go into Anypoint Studio and play around with simple flows (like I used above) and test yourself by throwing weird errors and handling them in creative ways. See if you can describe what will happen even before you debug the program and watch what happens. Make sure you have this down before continuing onwards. Next, brush up on any of the other areas that you scored low on during the first test. Only after doing this will you be ready to tackle the MuleSoft Certified Developer – Level 1 (Mule 4) exam.
Follow this methodology and you are almost certain to pass the exam, and trust me when I tell you that the feeling you get when you take the exam and finish and see that big PASS at the top of your score sheet, it will be the best feeling!