Error handling in Power Automate flows
Low Code solutions using Power Automate flows are becoming more and more popular in CRM Dynamics projects. But often low code solutions show maintenance issues because of the tendency of the developers (myself included of course) to neglect the exception handling aspects of the power automate flows under the assumption that "low code" also means "low error handling".
Our objectives
With this article I will cover the topic of how to handle errors in Power Automate flows and how to track them for a later analysis of them. What we need to achieve is a way of:
- capturing the errors that have been raised
- identifying the action that has failed
- getting a description of the reason for this failure
Let's get started.
Out-of-the-box features: some limitations
We will first have a look at the out-of-the-box features offered by Power Automate to check the flow executions.
When you need to verify when a flow has run and whether the flow execution was successful or not, you open the flow and you are presented a run history reporting the flow executions of the flow in the last 28-days:
In this view you do not get any details about the reason for the failure. You need to open the specific execution that failed by double clicking on it and you get the reason for the failure in the "Error Details" pane on the right hand side of the run view.
This way of inspecting the errors requires a periodical check of the flow executions: a time consuming action that can be easily avoided with the following solution.
Handling errors occurring in one action
Let's suppose to have a flow responsible for updating the owning team of an account according to the country where the company is based in.
We are going to trigger the flow when a new account is created or when its address is modified.
If the company address contains a USA address, the flow assigns the company to the USA Team, otherwise it will be assign to the Non-USA Team.
The flow structure is the following one:
If the update of the account owning team is successfully performed we want to send an email to confirm the successful assignment of its owner.
We also want to handle possible problems occurring in the update action called "Update Account owning team": in case an issue occurs while updating the account, we do not want the email to be sent, instead we want to catch the error, get its details and log the error for a later analysis.
At the moment we suppose to log the error in the Dataverse: I created a custom table called "Error Log" with the needed fields to store the error details. I will propose a slightly different solution later in the article.
The first thing we need to do to handle the update errors is to create a parallel branch after the update action as shown below.
After adding the branch we will have two actions running in parallel after the update action.
Our objective is to tell the flow to execute the branch on the right (Log error branch) only in case an issue with the previous action, "Update Account owning team", occurs. To achieve this we use the feature "Configure run after" of the "Log error" action.
Using Configure run after you can tell the flow to run the specific action if and only if an error has occurred in the previous action. You can configure “Run After” to almost all Power Automate actions by simply clicking on the three dots on the right corner of the action which you wish to configure and select “Configure run after” from the drop-down menu.
From there, you can select either one of the options or multiple options.
- Is successful – If the action runs successfully.
- Has failed – An action has any type of failure (except timeout).
- Is skipped – An action was skipped. Actions are skipped either when a condition is not met, or when a previous action before that fails.
- Has timed out – An action times out. This can happen if the call to the backend times out (120 seconds), or for long-running actions such as approvals, after 30 days.
We want to determine what the flow has to do when our update fails: we then select the "Has failed" option.
The error handling branch is displayed in the flow designer with a dotted line:
At this point our flow will send the email when the assignment is successfully performed and it will execute the "Log error" action if the update fails.
Having handled the possible update errors in the Log error branch, this flow will result to have successfully run even if an error while updating the account occurred.
To avoid this and ensure that the failed flow is marked as "failed" in the run history, add a Terminate action at the end of the error handling branch as shown in the picture below.
What we are still missing now is how to get the error details to be logged into the Dataverse.
To get this information we can rely on built in functions. The function we need in this case is called actions: This function takes the action name as input parameter and it returns an action's output at runtime in JSON format. The JSON format has approximately the following structure (further fields could eventually be present):
{
"name": "Update_Account_owning_team",
"inputs": {
"parameters": {
"entityName": "accounts"
}
},
"outputs": {
"statusCode": 400,
"body": {
"error": {
"code": "0x80040203",
"message": "Attribute: ownerid cannot be set to NULL"
}
}
},
"startTime": "2023-05-10T12:08:27.6333954Z",
"endTime": "2023-05-10T12:08:49.3524607Z",
"code": "BadRequest",
"status": "Failed"
}
From the JSON structure of the returned value I will extract the error message and I will put it into the "Error message" field of my custom entity "Error log" as shown in the picture below.
As the error message is nested in the JSON message, we need to use the following syntax to extract it :
actions('Update_Account_owning_team')?['outputs']?['body']?['error']?['message']
In our custom entity "Error log" we will now find all the errors occurred while updating the owning team of the accounts and the specific error message.
Handling errors in multiple actions
So far we handled the errors that could happen in a single action. A flow usually consists of a lot of actions and handling the errors using "Configure run after" for each action in the flow is not an efficient approach. In order to be able to handle the errors occurring in multiple actions the best solution is to group the actions using the so called Scope blocks.
In the scope action I can nest all my flow actions (the initialisation of the variables cannot be nested into a Scope action and must be kept outside, before entering the scope) as shown below. I will call this scope block "Try" (according to the error handling patterns in other programming languages).
After the Try block I will then add another Scope action that I will call "Catch": on the Catch action I will then set the "Configure run after" exactly as I did for the single action before. The Catch block will then be executed only if one or more actions included in the Try block fail.
In the Catch block we now need to get the details of what happened in the Try block: as in the Try block several actions could have thrown an error, we cannot use anymore the actions function as it refers to a specific action. Instead we will use the expression function called result. This function returns an array of objects consisting of the Input / Output information of the top-level actions in the specified scoped action. I now want to capture the Output of the actions in the Try block that have failed.
To do it I need to filter the array of objects returned by the "result" function to get the objects of the failed actions only. This can be achieved using the Filter array action. Finally we can use the action "Create HTML Table" to store the returned results in a structured way. We will rename this action "Select result".
The result expression is result('Try'), the status expression: item()?[status] while the Body is body('Filter_array_-_Status_=_Failed').
Once you have all your information stored in the "Select result" action you are ready to use it and put it into the proper fields of the Error Log record.
To include information about the flow in your error log, such as the name of the flow, you also need to use the power automate function called workflow. This function returns the Id, the name, the type and some other information about the running flow. We will use it for getting the flow name and for retrieving the flow information needed for building the Url of the specific execution or run where the log was written (you can find the syntax of it below).
This is how our catch block looks like:
Here the expressions of the fields above:
Action : outputs('Select_Result')?['body']?[0]?['Action']
Error message: outputs('Select_Result')?['body']?[0]?['Message']
Execution Url: concat('https://flow.microsoft.com/manage/environments/', workflow()?['tags']['environmentName'], '/flows/', workflow()?['name'], '/runs/', workflow()?['run']['name'])
Using Flow templates
When you create a new flow you get an empty page without any of the logic we just described. In order not to write the same error handling code every time you create a new flow you can prepare a flow including the Try-Catch pattern we just explained and use this flow as a template when you create a new one.
This is how the template flow would look like:
The Try block will contain the whole flow logic with its different actions.
The Catch error block can be the same for every flow and does not need to be changed when you start to develop a flow: this block will be like the one presented in the previous picture..
I also added a Catch success block where you can put the actions you want to take when the flow has successfully carried out its job, as sending an email or setting some flag or status to completed on other tables. You might want to track the executions of the flows in both cases, either it fails or it successfully runs: in this case you can log it in the same table with different status in the two catch blocks.
I mentioned before that using a custom entity to keep track of the flow failures is not an ideal solution because the Dataverse can quickly increase its size with a lot of error messages. An alternative solution would be using a Power BI Data Set or an external database for storing the error records, depending on your storage availability.
Well, we've come to the end of this article: I hope you enjoyed reading it and that it can be useful for your projects.
Meet you in the next Chronicle!
Error handling in Power Automate flows
Comments
response
a good topic and this works well if there are no nested actions. How would you get the error of a nested action? I have used a union for all the nested actions, but this is sloppy. I would like to try and find a way to dynamically union all the nested actions.