← Previously: Part 2: Approvals and escalation, building flows people actually respond to (13 Aug) → Next: Part 4: Premium connectors and licensing traps, where 'free' quietly stops being free (27 Aug)

What this post covers: why Configure run after is the only branching in Power Automate that can see an action's failure at all; what a retry policy actually retries and the whole category of errors it will never touch; how a Scope collapses twelve run-after settings into one; and the reason a flow with error handling bolted on is more likely to fail silently than one with none.

The three weeks nobody noticed

The flow ran every weekday at seven. It pulled rows from an Excel table in SharePoint, filtered them, and emailed a formatted summary to a distribution list. It had run for eight months without anyone thinking about it.

Then someone tidied up the SharePoint library and renamed the file. The next morning the flow failed. It failed again the morning after that, and every morning for three weeks, and the first anyone knew about it was when a manager asked, in passing, whether the Monday report had moved to a different address.

The interesting part is not that the flow broke. Files get renamed. The interesting part is the three weeks, and every mechanism in this post exists somewhere in that gap.

Configure run after is the only thing that can see a failure

Every action in a cloud flow carries a runAfter property, and that property holds a set of states rather than a single value. There are four: is successful, has failed, is skipped, and has timed out. When you add an action, Power Automate ticks is successful and leaves the other three alone.

That default is why a broken flow stops dead. The action fails, nothing downstream is configured to run after failure, so every subsequent action is marked skipped and the run itself is marked Failed. That is the correct behaviour, and it is the loud version. A red entry in run history is a flow doing its job.

Part 2 used this same mechanism for a timeout, which is a different state on the same property. An approval that expires has timed out, not has failed, and a branch ticked only for has failed will sit there doing nothing while you wonder why your escalation never fired. Failure and timeout are separate outcomes and you have to opt into each one deliberately.

The constraint that catches people out: Configure run after reads the immediately preceding action only. If you have three actions in a row and you set the third to run after has failed, it is watching the second one. The first could fail and the third would never notice, because the second was skipped, and skipped is not failed unless you say so.

Retry policies fire before you ever see the failure

By the time a failed run appears in your history, Power Automate has usually already tried again on your behalf. Every action that makes an external call ships with a default retry policy: exponential interval, four retries, waiting longer between each attempt.

What matters is the list of things it retries. A retry policy covers intermittent transport failures - HTTP 408, 429, and the 5xx range, plus connectivity exceptions. That is the entire list.

It does not cover 4xx errors other than 408. A 404 because the file was renamed, a 403 because someone's access was revoked, a 400 because the payload was malformed: none of those are retried, because retrying them is pointless. The server is not busy. The server is telling you no, and it will keep telling you no with the same conviction four attempts later.

This cuts both ways, and both directions are useful:

  • A run that failed instantly with a 404 was never a transient problem. No amount of raising the retry count will help. Something changed, and the flow is correctly reporting that it changed.
  • A run that took forty seconds instead of four probably hit a 429 and recovered. Nothing failed, nothing was logged as a failure, and you were being throttled the entire time. Left alone, that quietly becomes a real failure the day the load increases.

You can change the policy per action, under the action's Settings: None, Default, Fixed Interval, or Exponential Interval, with your own count and interval. Setting it to None is worth doing on any action that is not idempotent - if a retried "create item" call can produce two records instead of one, four retries is a data quality problem wearing a resilience costume.

A Scope is how you stop setting run after twelve times

Because Configure run after only watches one neighbour, error handling on a flow of any size becomes unmanageable fast. The fix is the Scope action, which groups a set of actions and reports a single outcome for the group. Anything inside it fails, the whole Scope fails.

The shape is the one you already know from every other language:

  • A Scope named Try holding the actual work.
  • A Scope named Catch immediately after it, with Configure run after set to has failed and has timed out, holding the logging and alerting.
  • Optionally a Finally Scope after both, ticked for is successful, has failed, and is skipped, for cleanup that has to happen either way.

Inside the Catch, the result() function is what makes this worth doing. It is reserved for container actions, and result('Try') returns an array describing every action in that Scope: its name, its status, its inputs, its outputs, and its error. So instead of an alert that says the flow failed, you can send one that names the action:

plaintext
first(
  filter(
    result('Try'),
    equals(item()?['status'], 'Failed')
  )
)?['error']?['message']

The first(filter(...)) shape matters more than it looks. A single upstream failure cascades - later actions get marked failed too - so the array will often have several failed entries and only the first one is the actual cause. Everything after it is wreckage.

The catch block that swallows the failure

Here is the part that turns a loud failure into a silent one, and it is done almost exclusively by people who were being conscientious.

If your Try Scope fails and your Catch Scope runs successfully, the run as a whole is marked Succeeded. Green tick in run history. The failure was caught, the catch worked, and as far as the platform is concerned the flow did what it was told.

That is technically correct and practically a disaster. You have taken a failure that would have shown up in run history, in the weekly digest, and in every failure count an admin might look at, and you have converted it into a success that only exists in whatever notification your Catch happened to send. If that notification goes to an email folder with a rule on it, the failure is now invisible.

The fix is one action: end the Catch Scope with a Terminate action, status Failed, with your own error code and message. The run is then marked Failed, it appears everywhere a failure should appear, and you still got the useful alert you wrote. Loud and informative rather than one or the other.

Why the notification never came

The three weeks in the opening scenario are not a story about someone being careless. They are a story about assuming Power Automate emails you when a flow fails. It emails you sometimes, and the conditions are narrower than most people expect.

Power Automate sends two different things. A per-run failure alert goes out shortly after a run fails, but only when the platform can identify a known, fixable root cause - a broken connection, a throttled action, a recognised connector error. If the failure is a general action failure with no specific fix available, no email is sent at all. That is deliberate: the alerts are meant to be actionable rather than merely noisy.

Then there are the qualifiers stacked on top. Cascade failures are filtered out, since they are not the root cause. After one per-run alert fires for a flow, there is a 28-day cooldown before another one can be sent for that same flow, so a failure repeating every morning generates one email, not twenty. Per-run alerts are not enabled for every flow by default, which is worth checking in flow settings right now rather than the day you need it. And admins never receive per-run alerts at all - only owners and co-owners do, so a flow whose owner has changed teams is a flow nobody is being told about.

The weekly failure digest does cover everything, including general action failures. It also arrives weekly, from an address most people have already filed into a folder. Three weekly digests is three weeks.

Which is the actual lesson: the notification you rely on has to be one you built. A Teams post to a channel your team reads, naming the flow, the failing action, and a link to the run, is worth more than every default alert combined, because you chose where it lands. This is the same failure mode as a flow with exactly one owner and no test of what happens when they are away (5 Aug) - the automation is fine, the human path around it is the single point of failure.

Frequently Asked Questions

Will a retry policy help with a file not found error?

No. Retry policies cover HTTP 408, 429, and 5xx responses plus connectivity exceptions. A 404 is not on that list, so the action fails on its first attempt with no retries at all. Raising the retry count changes nothing, because the file is still not there.

Can I set a retry policy on any action?

Only on actions that make an external call, which means connector operations and HTTP actions. Compose, Set variable, Condition, and other in-flow actions have no retry setting in Settings, because there is no remote call to attempt again.

What is the difference between has failed and is skipped in Configure run after?

Has failed means the previous action ran and returned an error. Is skipped means the previous action never ran, because something before it failed and the flow moved past it. In a chain of actions, only the first one to break is failed - everything after it is skipped, which is why a Scope is a better unit to watch than an individual action.

Does my Catch scope run when an approval times out?

Only if you tick has timed out as well as has failed. They are separate states on the same property and a timeout is not a failure. This is the single most common reason an error-handling branch that looks correct never fires.

Does Power Automate email me every time a flow fails?

No. Per-run alerts only fire when the platform identifies a known, fixable root cause, they are not enabled for every flow by default, and after one alert there is a 28-day cooldown before another can be sent for the same flow. The weekly digest catches the rest, once a week.

Should I use Terminate, or just let the run fail on its own?

If you have no Catch Scope, let it fail - the run is already marked Failed and reported. If you do have a Catch Scope, add Terminate with status Failed at the end of it, otherwise your error handling converts a visible failure into a run that reports success.

Where the series goes from here

Part 4: Premium connectors and licensing traps, where 'free' quietly stops being free (27 Aug) closes the series with the other thing that goes wrong quietly: a flow that works perfectly in testing and turns out to depend on a connector somebody has to pay for. Which connectors are standard, which are premium, and how to see the invoice coming before finance does.

Part 4 lands 27 Aug.