Power Automate Error Emails Nobody Actually Reads
The default 'flow failed' notification tells you almost nothing useful. Here's how to build error alerts that give you enough context to fix the problem without opening the flow run history.
Every Power Automate flow that can fail will fail. The question is whether you find out from a notification or from a user coming to you three days later.
The built-in failure notification — the one you get when you turn on “Send me an email notification when this flow fails” — tells you the flow name, the time, and a link. That’s it. You click the link, wait for the run history to load, expand each action, find the one that’s red, read the error. Useful if you have one flow. Useless if you’re responsible for twenty.
Here’s how to build notifications that actually tell you what went wrong.
What the Default Notification Doesn’t Tell You
The built-in email says: “Your flow ‘Sync Customer Records’ failed on 7/21/2026.”
What you actually need to know:
- Which action failed
- What the error message was
- What the input data looked like (so you can reproduce it)
- Which record or entity triggered the run
Without that, every failure is a manual investigation. With it, most failures are fixable in under a minute.
The Pattern: Try/Catch with a Scope
Power Automate doesn’t have native try/catch, but Scope actions give you the same thing. A Scope groups actions together and lets you configure what happens when any of them fail.
The structure:
Set the Catch scope’s “Run after” to has failed (uncheck “is successful”). Everything inside Catch only runs when something in Try went wrong.
Building the Catch Scope
Inside the Catch scope, add a Send an email or Post a Teams message action. The content is what matters.
A useful error email body:
Flow: @{workflow().tags.flowDisplayName}
Run: @{workflow().run.name}
Time: @{utcNow()}
Failed action: @{actions('Try').outputs.body.error.code}
Error message: @{result('Try')?[0]?['error']?['message']}
Trigger input:
@{triggerBody()}
Break down what each expression does:
workflow().tags.flowDisplayName— the flow’s display name, so a forwarded email still makes sense out of contextworkflow().run.name— the run ID, which you can paste directly into the run history URL to jump straight to itresult('Try')?[0]?['error']?['message']— the actual error message from the first failed action inside the Try scopetriggerBody()— the full trigger payload, so you know exactly what data caused the failure
The result() function is the key one. It returns an array of action results from inside a named scope. The first failed action is index 0. This is what the built-in notification never gives you.
Getting the Failed Action Name
result('Try') returns every action that ran inside the scope, not just the failed one. To get specifically the failed actions:
@{body('Filter_array')}
Add a Filter array action before your email, with:
- From:
result('Try') - Filter:
item()?['status']is equal toFailed
Now your email can include the action name (item()?['name']) alongside the error message. If you have a scope with ten actions and one fails, you know which one without opening the run.
Teams vs Email
For anything running on a schedule or triggered by system events, Teams is better than email. It’s faster to scan, easier to act on, and the Adaptive Card format lets you include a direct link to the run:
{
"type": "Action.OpenUrl",
"title": "Open run",
"url": "https://make.powerautomate.com/environments/@{workflow().tags.environmentName}/flows/@{workflow().name}/runs/@{workflow().run.name}"
}
That URL opens the specific run directly. No navigating through flow history, no searching by timestamp.
For flows triggered by user actions (a button press, a form submission), email to the flow owner plus a Teams message to a monitoring channel covers both bases — the user doesn’t need to see the technical error, but someone does.
One Notification Per Root Cause, Not Per Action
A common mistake: wrapping every individual action in its own error handler. You end up with five notifications for one logical failure because each action in a chain fails after the first one goes wrong.
One Scope around the entire logical unit, one notification on failure. If your flow has genuinely independent segments that should fail independently, use multiple Try/Catch pairs — but each pair sends one notification for its section, not one per action.
Practical Template
Subject: ❌ [@{workflow().tags.flowDisplayName}] failed — @{formatDateTime(utcNow(), 'dd MMM yyyy HH:mm')} UTC
Flow: @{workflow().tags.flowDisplayName}
Environment: @{workflow().tags.environmentName}
Run ID: @{workflow().run.name}
Error: @{result('Try')?[0]?['error']?['message']}
Trigger data:
@{triggerBody()}
Open run: https://make.powerautomate.com/environments/@{workflow().tags.environmentName}/flows/@{workflow().name}/runs/@{workflow().run.name}
The subject line includes the flow name and timestamp so it’s scannable in an inbox without opening it. The run link means you can go directly to the evidence. The trigger data means you can reproduce the failure without waiting for it to happen again.
What You Don’t Need to Build
You don’t need a separate “monitoring flow” that checks other flows. You don’t need a Log Analytics workspace. You don’t need a custom connector to a logging service. For most Power Platform solutions, a well-designed Catch scope per flow is enough — it tells you what failed, why, and what data was involved, which is all you need to fix it.
The flows that genuinely need centralised monitoring are the high-volume ones that fail dozens of times per hour on bad data. For those, a Dataverse logging table with an append step in every Catch scope is worth it — you get a queryable history rather than an inbox full of emails. But that’s a separate problem from the one most people have, which is just not knowing when things break.
Start with a Catch scope on every flow you care about. That alone puts you ahead of most implementations.
Related articles
MCP Is Just a Connector Standard. Here's Why It Matters.
Model Context Protocol is getting traction as a standard way to connect AI agents to tools and data. Here's what that means for Power Platform right now — the patterns that work, and the parts still being figured out.
Connecting D365 to Everything Else
A practitioner's guide to every integration option Dataverse offers — Webhooks, Service Bus, Virtual Tables, Dual-Write, Power Automate, Custom APIs, Web API, and the .NET SDK. When to use each, when to avoid them, and how to pick the right one.
Power Platform Licensing: What I Wish Someone Told Me on Day One
Licensing isn't a procurement problem — it's an architecture decision. Here's the mental model every solution architect needs before starting a Power Platform project, with real scenarios and a pre-project checklist.