*** NOTE: ALL INFORMATION IS ACCURATE AT DATE OF PUBLISHING ***

If you create flows for your organisation, or for clients on projects, you’ve no doubt improved your skills over the years. This might mean you started including steps in the flow to notify you of any failures. Or you might have just come in to a project with tens or even hundreds of flows and it might not be realistic to go through and add new actions to each to check for errors. So how can you find out when flows fail without going through and editing them all? This was my own recent mini challenge to solve. I wanted to know when flows might be failing and send an email to my self so I could dig in and find out. This post will show how you can create a notification for failed flow runs in power automate.

I searched for a way to do this for a while, but was lead back to adding in a notification step to each flow which just wasn’t practical with so many flows as part of the project I was on. I also wanted a summary of the flow runs that failed rather than one each time. I also found things using PowerShell and the Dataverse API. I wanted something a bit simpler if possible. Then I found this link from Vaibhav Ghorpade sharing a new preview feature from Microsoft. using the Cloud flow run history in Dataverse, I could then create a flow to find any failed runs from the previous day and send an email.

First things first, go to the Power Platform Admin Centre, then Environment Settings and find the Cloud flow run history in Dataverse. You can store the flow run logs for 28 days, 14 days or 7 days, or disable in completely. The length of time you want to store them I guess depends on what you want to do with them. If you are just sending an email and have no other need for the data to be in Dataverse, 7 days is probably good. There is no setting here to determine which runs get stored, so keep in mind it will store all of them (Failed, Succeeded, Cancelled etc).

Click to view in detail

Once you turn it on, you would be able to go to the Flow Runs table and see the rows of data being stored.

Click to view in detail

Now let’s build the flow. This will be a scheduled cloud flow that I will run at 10 AM each day.

Click to view in detail

The first action will be to initialize a variable that needs to be an Array. This will be used throughout the flow to populate information about the flow runs that failed and put them in to a nice table for the email.

Click to view in detail

The next step is a list rows action from the Dataverse connector using the Flow Runs table. You don’t need to select any specific columns but I only wanted specific things, so I’ve set it to these ones:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
duration,endtime,errorcode,errormessage,flowrunid,name,starttime,status,triggertype,_workflow_value,_ownerid_value,workflowid,ownerid
duration,endtime,errorcode,errormessage,flowrunid,name,starttime,status,triggertype,_workflow_value,_ownerid_value,workflowid,ownerid
duration,endtime,errorcode,errormessage,flowrunid,name,starttime,status,triggertype,_workflow_value,_ownerid_value,workflowid,ownerid

For the filtering, I only wanted those that ran yesterday and the status is Failed. I’m sorting them in descending order of when they were created but you can sort by the name or anything else if you choose. This really depends on the order in which you wish to see them in the email.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
(Microsoft.Dynamics.CRM.Yesterday(PropertyName='createdon') and status eq 'Failed')
(Microsoft.Dynamics.CRM.Yesterday(PropertyName='createdon') and status eq 'Failed')
(Microsoft.Dynamics.CRM.Yesterday(PropertyName='createdon') and status eq 'Failed')
Click to view in detail

Now that we’ve searched for the failed flows, we only need to continue if we have one or more failed flows. To do this, add a condition and then add an expression to check the length or number of records found. If you have left the default name of List rows in the step above, your expression will be the same as in the screenshot below. However, if you’ve renamed it, just add that but with the words separated by underscores like you see in the text below. Select the is greater than option from the middle drop down, then add zero in the last box. We want the number of rows found to be greater than zero to continue.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
length(body('Failed_Flow_Runs_From_Yesterday')?['value'])
length(body('Failed_Flow_Runs_From_Yesterday')?['value'])
length(body('Failed_Flow_Runs_From_Yesterday')?['value'])
Click to view in detail

The no path of your condition branch will be empty. If the number of rows found is not greater than zero, no email needs sending. Now add in an Apply to each step in the yes branch, and use the value output from the list rows step to get started.

Click to view in detail

First we are going to build a link to the flow run that failed using a Compose step. This will mean we can add a direct link so that the person receiving the email can just click on it to see the information and figure out why it failed. The inputs for the Compose are using a bit of HTML to create a hyperlink, then creating the full path using the GUID for the environment (workflow()[‘tags’][‘environmentname’]). The workflowid is used to get the GUID for the flow, then the name is used to get the GUID for the specific flow run. We can then use the workflow name (name of the flow) as the thing to display in the email for the hyperlink.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<a href="https://make.powerautomate.com/environments/@{workflow()['tags']['environmentName']}/flows/@{items('For_Each_Flow_Run')?['workflowid']}/runs/@{items('For_Each_Flow_Run')?['name']}">@{items('For_Each_Flow_Run')?['workflow/name']}</a>
<a href="https://make.powerautomate.com/environments/@{workflow()['tags']['environmentName']}/flows/@{items('For_Each_Flow_Run')?['workflowid']}/runs/@{items('For_Each_Flow_Run')?['name']}">@{items('For_Each_Flow_Run')?['workflow/name']}</a>
<a href="https://make.powerautomate.com/environments/@{workflow()['tags']['environmentName']}/flows/@{items('For_Each_Flow_Run')?['workflowid']}/runs/@{items('For_Each_Flow_Run')?['name']}">@{items('For_Each_Flow_Run')?['workflow/name']}</a>
Click to view in detail

You only need this if you want the name of the person that owns the flow to show in the email. Otherwise you can ignore this step. I tried getting it from the list rows step but it always showed up empty. So here I am using a Get a row by ID step for the Users table, then using the Owner value from the List rows step and selecting the fullname column only.

Click to view in detail

Now we add the details for each flow run that failed and using an Append to array variable action we start adding in the details for our table of flow runs. You can include whatever you like, but for mine I am using the Outputs of the Compose step where I created a hyperlink, then showing the start date/time of the flow run, the duration, then the name of the owner of the flow. Notice that the Duration needs an expression so it shows in hours/mins/seconds or ms depending on how long it is. You don’t have to do this, but if you don’t it will just be a long string of numbers in milliseconds only,

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
"Flow": "@{outputs('Link_To_Flow')}",
"Date": "@{formatDateTime(items('For_Each_Flow_Run')?['starttime'], 'dd-MM-yyyy hh:mm tt')}",
"Duration": "@{if(less(items('For_Each_Flow_Run')?['duration'], 1000),
concat(items('For_Each_Flow_Run')?['duration'], ' ms'),
formatDateTime(addSeconds('1970-01-01T00:00:00Z', div(items('For_Each_Flow_Run')?['duration'], 1000)), 'HH:mm:ss')
)
}",
"Owner": "@{outputs('Get_a_row_by_ID')?['body/fullname']}"
}
{ "Flow": "@{outputs('Link_To_Flow')}", "Date": "@{formatDateTime(items('For_Each_Flow_Run')?['starttime'], 'dd-MM-yyyy hh:mm tt')}", "Duration": "@{if(less(items('For_Each_Flow_Run')?['duration'], 1000), concat(items('For_Each_Flow_Run')?['duration'], ' ms'), formatDateTime(addSeconds('1970-01-01T00:00:00Z', div(items('For_Each_Flow_Run')?['duration'], 1000)), 'HH:mm:ss') ) }", "Owner": "@{outputs('Get_a_row_by_ID')?['body/fullname']}" }
{
  "Flow": "@{outputs('Link_To_Flow')}",
  "Date": "@{formatDateTime(items('For_Each_Flow_Run')?['starttime'], 'dd-MM-yyyy hh:mm tt')}",
  "Duration": "@{if(less(items('For_Each_Flow_Run')?['duration'], 1000), 
   concat(items('For_Each_Flow_Run')?['duration'], ' ms'), 
   formatDateTime(addSeconds('1970-01-01T00:00:00Z', div(items('For_Each_Flow_Run')?['duration'], 1000)), 'HH:mm:ss')
)
}",
  "Owner": "@{outputs('Get_a_row_by_ID')?['body/fullname']}"
}
Click to view in detail

Now you are done with the Apply to each so you can minimise that and add in a step outside of the Apply to each but still in your yes branch from the condition earlier. Here we need the Create HTML table action. Use your Flow Runs variable as the From value and keep the Columns as Automatic, we’ve already determined those.

Click to view in detail

As with a few other items, this isn’t needed but can be used to make the table look a bit prettier by adding in a Compose step, then setting some CSS to style the table, and including the Output from the Create HTML table step above at the end. If you want to know more and get an example to start with, check out this post from my friend Ryan Maclean on Power Automate HTML Table Styling.

Click to view in detail

Now this next step is necessary to clean up the HTML from the step where we created a nice link to the flow run. It gets a little broken when we use the Create HTML table step, but that’s OK, we can fix it. And thanks to Strategy 365 as I found a post of theirs that provided the expression. Where you see Compose below, that should be whatever you named the step above where you added your CSS. If you didn’t add that step, it should be whatever you named the Create HTML table step. This will fix specific characters in the link HTML.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
replace(replace(replace(outputs('Compose'),'&lt;','<'),'&gt;','>'),'&quot;','"')
replace(replace(replace(outputs('Compose'),'&lt;','<'),'&gt;','>'),'&quot;','"')
replace(replace(replace(outputs('Compose'),'&lt;','<'),'&gt;','>'),'&quot;','"')
Click to view in detail

Next we will add an action from the Power Platform for Admins connector. Click on the Get Environment as Admin. Note that the only reason I am using this step is so that I can get the name of the environment to use in my email so I know where the failed flows came from. This is really useful if you work with an organisation with multiple environments.

Click to view in detail

Use this expression to get the environment name.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
workflow()?['tags']?['environmentName']
workflow()?['tags']?['environmentName']
workflow()?['tags']?['environmentName']
Click to view in detail

Finally, use whatever step you want for sending out your email. I’ve got the standard send an email notification action but you could of course use Outlook, SendGrid or whatever works for your organisation. You can set up the email however you choose, but I am adding in the date of when the failed flows are from, and showing the name of the environment in the subject.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Failed Flows From @{formatDateTime(addDays(utcNow(), -1), 'dd-MM-yyyy')} - @{outputs('Get_Environment_as_Admin')?['body/properties/displayName']}
Failed Flows From @{formatDateTime(addDays(utcNow(), -1), 'dd-MM-yyyy')} - @{outputs('Get_Environment_as_Admin')?['body/properties/displayName']}
Failed Flows From @{formatDateTime(addDays(utcNow(), -1), 'dd-MM-yyyy')} - @{outputs('Get_Environment_as_Admin')?['body/properties/displayName']}

Then in the email body, I am showing the number of the flows that failed in the specific environment, then using the output from the final compose step where we fixed the broken URLs. For the number of flows, I want it to be accurate English, so if there was only 1 flow that failed, I am showing “There is 1 flow“, and if there is more than one, I am showing “There are X flows”.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@{if(equals(length(body('Failed_Flow_Runs_From_Yesterday')?['value']), 1),
'There is 1 flow run that failed yesterday',
concat('There are ', length(body('Failed_Flow_Runs_From_Yesterday')?['value']), ' flow runs that failed yesterday')
)
} in the @{outputs('Get_Environment_as_Admin')?['body/properties/displayName']} environment.
@{outputs('Fix_URLs')}
@{if(equals(length(body('Failed_Flow_Runs_From_Yesterday')?['value']), 1), 'There is 1 flow run that failed yesterday', concat('There are ', length(body('Failed_Flow_Runs_From_Yesterday')?['value']), ' flow runs that failed yesterday') ) } in the @{outputs('Get_Environment_as_Admin')?['body/properties/displayName']} environment. @{outputs('Fix_URLs')}
@{if(equals(length(body('Failed_Flow_Runs_From_Yesterday')?['value']), 1), 
   'There is 1 flow run that failed yesterday', 
   concat('There are ', length(body('Failed_Flow_Runs_From_Yesterday')?['value']), ' flow runs that failed yesterday')
)
} in the @{outputs('Get_Environment_as_Admin')?['body/properties/displayName']} environment.
@{outputs('Fix_URLs')}
Click to view in detail

Now once the flow runs, if you have any failed flow runs from the previous day you should get a nice little

Click to view in detail


Check out the latest post:
View Open Leads With Scores In Customer Insights - Journeys


This is just 1 of 497 articles. You can browse through all of them by going to the main blog page, or navigate through different categories to find more content you are interested in. You can also subscribe and get new blog posts emailed to you directly.




9 thoughts on “Notification For Failed Flow Runs In Power Automate

  1. Just as an addition, in case someone wants to receive an email at shorter intervals: I have observed that it can take several minutes for the flow run record to appear in Dataverse after a flow execution. When filtering by time, such as the last 4 hours, records may be missing because there are flow executions from the last few minutes that are not yet there and will not be filtered afterwards. Therefore, be careful that the duration is far enough in the past (like your example) or overlaps with the last one.

  2. Hi Megan,

    Thanks for this amazing blog. It is very cool that we can have a notification for a failed run. In my case, the cloud flow run history was already enabled. When I checked the table, I saw logs, but only for system flows. I don’t see any logs for the flows that different users have created. Do you know if there is a setting somewhere I am missing? I cannot find anything on the internet, except the official article from Microsoft.

    I have created the flow, and everything works perfectly fine but it is only the flows that are created by the system.

    Thanks!

    1. Hi Ahmad, there is no setting that dictates whose flows you can see in the flow runs table. The environment where I set this up has flows from lots of users and we can all see the failures from all of the flows. You might need to ask Microsoft Support. Also, you might want to check the Flow Runs table to see if you are seeing more data there and see if it matches up with the data you are seeing in your flow with the failures.

    2. Hi Megan,

      I have already compared the data from the flow and table itself in the Power Apps portal, they are the same and I don’t see the flow runs. I would make a ticket with MS.

      Thanks!

    3. Hi Megan,

      Thanks for the hint. I see the limitation now. It does not work for shared flows, and most of our flows are shared between users, so it is not supported at the moment. I have already given all users the read access, so hopefully I will see some flow runs.

      Thanks!

    4. no problem, glad it helped. shame there is a limitation but it is still in preview, so room and time for change perhaps!

Leave a Reply

Your email address will not be published. Required fields are marked *