Exception handling is key to all automations, as no matter how hard we try there will always be unexpected behaviour
The run after isn't the most intuitive exception handling I've seen (Blue Prism does it better), but once you used it a few times it's not too bad.
How To
Exception handling is configure in the run after setting. As good practice I recommend in most cases you should catch fails and timeouts.
Parallel branches can be used to divert and handle exceptions, with either the branch ending the flow or rejoining after handling the exception.
Scopes
are key to exception handling, as they create a block that can be handled the same i.e if ay action fails within the Scope
it will trigger the exception handling. Additionally wrapping all your exception actions within its own Scope
ensures all those actions run.
The exception detail can be found and used with the following 2 expressions
actions() - For action exception
result() - For container (e.g scope/condition)
I recommend result() as catching exceptions from Scopes
is easier. It returns an object of data:
{
"from": [
{
"name": "Condition_Has_Attachment",
"inputs": {
"expressionResult": true
},
"startTime": "2023-01-08T18:25:51.8392646Z",
"endTime": "2023-01-08T18:25:52.05801Z",
"trackingId": "9999ec4-9999-9999-9999-33231c464c09",
"clientTrackingId": "0858599999373166578219962773CU98",
"status": "Succeeded"
},
{
"name": "Delete_email_(V2)",
"inputs": {
"host": {
"apiId": "subscriptions/999931-9999-9999-9999-0b5042c8bada/providers/Microsoft.Web/locations/westus/runtimes/unitedstates-002/apis/office365",
"connectionReferenceName": "shared_office365_1",
"operationId": "DeleteEmail_V2"
},
"parameters": {
"messageId": "AAMkAGU4YmNlMGY4LWFjZDYtNDczMi05NDdhLTk0M2I5MjAzZmRmZgBGAAAAAABO9BLUWcxCTZzsiK9ugUPMBwC8DS3ucP99993qafeZHAAAAAAEMAAC8DS3ucPOGTZXMG3qafeZHAAAfSjgpAAA="
}
},
"outputs": {
"statusCode": 404,
"headers": {
"Transfer-Encoding": "chunked",
"Vary": "Accept-Encoding",
"Strict-Transport-Security": "max-age=31536000",
"request-id": "999956fb-9999-9999-9999-3d70bcc74838",
"client-request-id": "999956fb-9999-4823-9999-3d70bcc74838",
"x-ms-ags-diagnostic": "{\"ServerInfo\":{\"DataCenter\":\"West US\",\"Slice\":\"E\",\"Ring\":\"4\",\"ScaleUnit\":\"001\",\"RoleInstance\":\"BY1PEPF00003E18\"}}",
"Timing-Allow-Origin": "*",
"x-ms-apihub-cached-response": "true",
"x-ms-apihub-obo": "false",
"Cache-Control": "private",
"Date": "Sun, 08 Jan 2023 18:26:20 GMT",
"Content-Type": "application/json",
"Content-Length": "370"
},
"body": {
"error": {
"code": "ErrorItemNotFound",
"message": "The specified object was not found in the store., The process failed to get the correct properties.",
"innerError": {
"date": "2023-01-08T18:26:21",
"request-id": "999956fb-9999-9999-9720-3d70bcc74838",
"client-request-id": "999956fb-9999-9999-9999-3d70bcc74838"
}
}
}
},
"startTime": "2023-01-08T18:25:52.05801Z",
"endTime": "2023-01-08T18:26:21.2375136Z",
"trackingId": "c8481347-8c99-4334-a6eb-6153ce55344e",
"clientTrackingId": "08585284045373166578219962773CU98",
"code": "NotFound",
"status": "Failed",
"retryHistory": [
{
"startTime": "2023-01-08T18:25:52.05801Z",
"endTime": "2023-01-08T18:26:15.9333579Z",
"code": "BadGateway",
"clientRequestId": "db499999-9999-9999-9999-f9999c3d7232"
}
]
}
],
You can simply turn it to a string and pass it back, or use a Parse JSON
and HTML/Markdown table to display it nicely.
The table is the easiest, I pass the action name, error reason and error description:
result('Main')
item()['name']
item()?['error']?['code']
item()?['error']?['message']
There are 3 different types/categories for exception handling
- Process
- Business
- System
Process
Process exceptions are exception handling used as logic. So it's almost used as a condition, with the expectation the process will continue i.e. its not a real exception.
These are generally called try, catch, continue, and although very useful I'm personally not a fan. I would rather try standard logic to check and then not try, rather then try and deal with the error.
Business
Business exceptions are exceptions generated by input data, and are generally in loops. What's key about them is we don't want to end the process, we just want to flag that item as exception, and continue with the other items.
These generally only have one action, and may not need a Scope to contain them. Though to ensure the process continues the action after the loop would have to have run after Success and Failed enabled. A good example would be a to add a row to a SharePoint list, so that those items can be manually processed/corrected.
System
System exceptions are when something unexpected in the flow happens. Either a connector is failing due to the api or a key file can't be found. In these cases the process should carry out any clean up actions (e.g delete temp files) and escalate. This could be like a business exception with entry in a SharePoint list, but generally as its more serious I would recommend a teams message or email.
A good tip for system exceptions are:
Wrap the entire process in a Exception
Having a 'Main' Scope
that contains every action means that a system exception after can catch any error in process. Although we mostly get failed emails from Power Automate, this is no good if running under a Service Account
Terminate after handling
Use the Terminate
action set to fail, this allows you to still identify failed runs in the logs
Run link
The below expression creates a link to the failed run, so if added to the message/email, the user can click the link and go straight to the faile run. An added bonus is these links work longer then the normal retention (last 100 runs, I've got a link over a 1000 runs previous) and it can be accessed by environment admins without sharing the flow.
concat('https://make.powerautomate.com/manage/environments/', workflow()?['tags']?['environmentName'], '/flows/', workflow()?['name'], '/runs/', workflow()?['run']['name'])
Top comments (0)