Basic Exception Handling in Power Automate Cloud Flows

Power Automate is a powerful tool that works well for integration and even automating workflows. Citizen developers have started adopting Power Automate to automate some of their day-to-day activities. One of the most important parts of a traditional solution development strategy is error and exception handling.
Automation would typically face two kinds of errors/exceptions: Application Error and Business Exception. A business exception refers to any scenario where the process does not meet the defined business rules that the automation was built to include. An application error refers to any ‘unknown’ (unrelated to business rules) error that occurs within the execution of the solution.
Even as citizen development takes off, there remain to be many advantages realised by implementing exception handling. But how do we do it in Power Automate? Here’s how…
Create a Flow
Navigate to Power Automate Cloud and create a new, blank, flow. As part of the exception handling strategy, a variable, named varErrorOccurred, will be used to store a boolean value indicating that the flow either ran successfully or an error occurred, in which case the flow did not run successfully. Scopes will be used to group process steps separately from the steps to be executed when an error occurs. This refers to the try…catch approach (which often includes a finally code block as well). The try block contains all of the process steps that should be executed while the catch block contains all of the steps to be executed if an error occurs during any of the process steps. The finally block contains any steps to be executed, regardless of whether or not any exceptions occurred.
Setup Scopes for Exception Handling
Let’s start by adding three scopes to the flow and name them Try, Catch and Finally:

Exception Handling in Power Automate Cloud
Next, the Catch scope should be set up to run when an error occurs in the Try scope. To do this, click on the three dots in the top-right corner of the Catch scope and select Configure Run After:

Then set up the Finally scope to run after any state by following the same steps above, but this time, for the Finally scope:

Now you can add your actions into the relevant scopes and test your workflow.
Note: You cannot yet initialise variables within a scope, variables need to be initialised at the highest level of the flow, so ensure that you add any Initialise variables actions before the Try scope.
What Goes in Each Scope
Once the scopes are set up, here's a guide to what usually goes where:
- Try: All of your process steps, as they would have been without any exception handling.
- Catch: Set varErrorOccurred to true, capture the error details and notify whoever needs to know (an email or Teams message to the process owner or support team works well). If the process may have partially completed, this is also the place to clean up or roll back.
- Finally: Anything that should happen regardless of the outcome, like writing an audit/log entry or updating the status of the item being processed.
When configuring Run After on the Catch scope, tick has failed and has timed out so that both kinds of failure are caught. The Finally scope should have all of the options ticked.
Getting the Error Details
Knowing that something failed is only half the story; you'll want to know what failed. The result() expression returns the results of all of the actions inside a scope, so you can add a Filter array action in the Catch scope with:
- From:
result('Try') - Condition:
item()?['status']is equal toFailed
The output of the Filter array action gives you the failed action(s), including the action name and the error, which you can then use in your notification or log, for example:
first(body('Filter_array'))?['name']
first(body('Filter_array'))?['error']?['message']
Note: Use the scope's internal name in result(), so if your scope is named something other than Try, or has spaces in the name, replace the spaces with underscores (e.g. result('Try_Scope')).
Making Sure Failed Runs Still Show as Failed
Once the Catch scope has handled the error, the flow run will show as Succeeded in the run history, which makes it easy to miss failures. To avoid this, add a Condition at the end of the Finally scope (or after it) that checks varErrorOccurred and, if it is true, use a Terminate action with the status set to Failed. That way your error handling still runs, but the run history accurately reflects what happened.
Testing Your Exception Handling
It's worth testing the unhappy path as well as the happy one. A simple way to do this is to temporarily add an action to the Try scope that is guaranteed to fail (like a Compose action with an expression that divides by zero, e.g. div(1, 0)) and confirm that the Catch and Finally scopes behave the way you expect.