One of the best milestones as "Makers" in the Power Platform, is that moment when we understand that things may not always go as planned, and then build in a way to accommodate, and possibly mitigate that. There are loads of articles on a method called "Try, Catch, Finally" and is very familiar to "Classically Trained" or "Code First" developers. I'll spend a little bit of time talking about that, and may break this up into a few parts since it's a big subject...but it is absolutely something you can do, and probably should do with all of your flows.
The Foundation
"Try, Catch, Finally" was new to me a while ago, and not something I set out to learn. Like many other things in Power Automate, it was something I picked up along the way that helped solve an immediate problem, and now I integrate it into most of the work that I do. The concept is like this:
Try - Just as the name suggests, this is where our flow tries to do "the thing" (whatever that happens to be in your use-case).
Catch - What should the flow do if things go sideways? In many of my cases, it's to send an email alerting me there is a problem. This is generally quicker than waiting for Power Automate to send a summary on its own. We usually will leverage the built-in "Configure Run After" functionality (don't freak out if you're not familiar with that, we'll talk about it).
Finally - This step is done in either case. It can be a status update in a log, or the email can be in this step, telling us that things went great, or there was a horrible problem. Totally up to you. Since most of my things are simple, I am only sending an email if things go wrong, so you may not always see a "Finally" part.
The Beginning
To start, we'll explore the "Configure Run After" functionality since this is the foundation of error handling in Power Automate. Every flow will have a trigger and at least one action. Configure Run After is most helpful on any action after that first action because you're telling that subsequent action, "Run this step if the previous one does not run successfully". On any action, click on the ellipses ("...") near the top right of that action, and select "Configure Run After". This will bring up a window similar to what's below, describing exactly what will happen:
To send an email if the error is not successful, we can uncheck "is successful" and check any (or all) of the options below that, depending on our use-case. That way, we can send an email to ourselves (or IT, or whoever) if something goes wrong in our flow. In the beginning, my emails would just have the name of the flow in the subject, with a body that said to look at past runs because of an error (more on that later).
But if we have to do that for every action that might possibly fail, how will that work? This is where a thing called "Scope" comes in. Think of Scope as a container, and you can put a series of actions inside that container. It's a great way to group actions based on functionality or whatever you'd like, but since the whole scope will fail if one action inside it fails, we're going to use it as part of our "Try" block and put all of our actions inside of it (or, as many as we can).
For the example of this article, I've only got a single action inside the Try scope, but you can have many, many more. For the action, I'm using something that I know will fail (a compose with a Divide by Zero expression)...
div(1,0)
...so that we can test the Catch part of the flow.
Also, I recently discovered that you can use a Scope inside of another Scope.
After the "Try" scope, let's add another scope and rename that to "Catch" (there's a great reason to keep the names standard...more on that later.
Since we're going to build something similar to the email above, we'll need to have a few steps to gather and format all of the information. The first step inside of our "Catch" scope is a compose action that stores the base URL to hyperlink to Power Automate. I have this in a separate step so that I can easily move this block from a 'normal' tenant, to a government tenant where the base URL is different. This saves me the trouble of having to find all of the hyperlinks and change them when I copy the block.
Following that is a Filter Array step that is going to take the results of the "Try" block and only filter anything that has a status of "Failed".
The Filter Failed Items step is going to generate a bunch of information that we won't really need for our email. There are other ways to grab just the bits that you need, but I like to use a "Select" after the filter, to only get the necessary fields of data:
As you can see, I am using the body of the Filter Array step as the "From" on the Select, and then I've created my own "keys" on the left side of the map. The right side expressions look like this (again, other ways to do this. This is just the way that works best for me):
Action = item()?['name']
Reason = item()?['error']?['code']
Details = item()?['error']?['message']
Then, we'll take the output of that step and use a "Create HTML table" action to turn that into a nice table for our email. The table won't look super awesome, so I love to copy April Dunnam's process of building a good looking table. The final expression for that step is here:
replace(replace(body('Create_HTML_table'), '<table>', '<table border="1">'), '<td>', '<td style="padding:20px">')
The next two steps are Compose actions, as well. These will just get some details about the flow for us to include in the email (flow display name, environment ID, and flow run ID). The expression in that compose is just workflow().
The last compose in that screenshot is building the URL for the flow run that errored out. This is very handy when troubleshooting so you don't have to click on all of your runs to see which one generated which error. That expression uses our URL preamble from above, and adds the environment name (ID), Flow name (ID), and Flow run Name (ID). When clicked, this URL will take the user directly to the run that errored out.
concat(
outputs('Compose:_URL_Preamble'),
outputs('Compose:_Get_Workflow_Details')?['tags']?['environmentName'],
'/flows/',
outputs('Compose:_Get_Workflow_Details')?['name'],
'/runs/',
outputs('Compose:_Get_Workflow_Details')?['run']?['name']
)
The next step is really useful if you're working in multiple environments. If you have appropriate permissions, we can take the environment ID and get the Display Name using the action "Get Environment as Admin" and use that information in our email subject.
For the environment, you'll choose "Enter Custom Value" and then use the expression below to leverage the Environment GUID that comes with the workflow() payload:
outputs('Compose:_Get_Workflow_Details')?['tags']?['environmentName']
When writing HTML directly to an email using the Send an Email (V2) action from Office 365 Outlook, be sure to click the </> button first to put the input in the correct mode.
Finally, note that I used a terminate action to end the flow as "Failed". This is because when flow runs a successful Catch block, it considers that a successful flow. We want an easy way to identify flows that failed when looking at prior runs.
Remember that you can click on the ellipses of the Catch scope and select "Copy to my clipboard" and then paste that whole block into other flows. This works VERY well if you've kept the step/scope names consistent from flow to flow.
I hope you found pieces of this useful, and are having fun building things in Power Automate.
Good luck!
Hi, I'm trying to reproduce your example but in the "Filter Failed Items" step, where did you get the "Status" field from? Based on the blue icon it looks like it came from the trigger, which does not include that field. And wouldn't the status need to come from the "Compose" action in the "Try" scope? I tried entering the expression manually instead as outputs('Compose')['status'] but it just results in an error when testing.