Power Automate Conditions Loops and Error Handling
A flow that only runs in one straight line handles only the simplest scenarios. Real business logic requires decisions — do this if approved, do that if rejected. It requires loops — process every record in a list. It requires error handling — when something goes wrong, do not silently fail, alert someone and log the problem. This topic teaches you the control structures that make flows smart, flexible, and robust.
Condition: The Flow's Decision Point
A Condition evaluates a statement and routes the flow into one of two branches: Yes (true) or No (false). You use Conditions whenever the next action depends on a value — an approval outcome, a field's content, a number threshold, or a date comparison.
Building a Condition
Click the plus icon between any two steps and choose Add an action. Search for "Condition" under the Control group. The condition step shows three parts: a left value, a comparison operator, and a right value.
CONDITION BUILDER
Left value: [dynamic content or expression]
Operator: is equal to / is not equal to / is greater than /
is less than / contains / does not contain /
starts with / ends with / is null / is not null
Right value: [static text, number, or dynamic content]
Example 1: Check approval outcome
Left: [Outcome] (from Start and wait for an approval)
Operator: is equal to
Right: Approve
Example 2: Check if an amount exceeds a threshold
Left: [Amount] (from Dataverse row)
Operator: is greater than
Right: 50000
Example 3: Check if a field is empty
Left: [ManagerEmail] (from previous step)
Operator: is not null
Right: (leave empty)
Multiple Conditions with AND / OR
Click Add inside the condition to add more rules. Use AND when all rules must be true simultaneously. Use OR when any one rule being true is enough.
AND example: Alert only if Priority is High AND Status is Open [Priority] is equal to High AND [Status] is equal to Open OR example: Escalate if overdue OR if priority is Critical [DueDate] is less than [utcNow()] OR [Priority] is equal to Critical
Nested Conditions
Place a Condition inside another Condition's Yes or No branch to create multi-level decision trees. Nest carefully — deeply nested conditions become hard to read and maintain. When you have more than three levels, consider using a Switch control instead.
Switch: Cleaner Multi-Branch Logic
A Switch evaluates one value against multiple possible cases and routes to the matching branch. It is cleaner than nested IFs when you have three or more possible outcomes for a single field.
SWITCH vs. NESTED CONDITIONS
Nested Conditions (hard to read):
If Status = Open →
If Status = Closed →
If Status = Pending →
If Status = Cancelled → ...
Switch on Status (clean and readable):
┌─────────────────────────────────────┐
│ Switch: [Status field] │
├──────────┬──────────┬──────────┬────┤
│ Open │ Closed │ Pending │ * │
│ (branch) │ (branch) │ (branch) │def │
└──────────┴──────────┴──────────┴────┘
* = Default case (runs if no other case matches)
To add a Switch, click the plus button, choose Add an action, search "Switch" under Control. Set On to the dynamic content you want to evaluate (e.g., Status field). Click Add Case for each possible value and add actions inside each case. Add a Default case for unexpected values.
Apply to Each: Looping Over a List
Apply to Each processes every item in an array (a list of records, a list of emails, a list of file names) by running the same set of actions for each item. Most commonly, the array comes from a "List rows" action on Dataverse or SharePoint.
How Apply to Each Works
APPLY TO EACH FLOW
List rows (Dataverse):
Returns: [
{ Title: "Fix printer", Priority: "High", AssignedTo: "priya@co.com" },
{ Title: "Reset password", Priority: "Low", AssignedTo: "james@co.com" },
{ Title: "Install software", Priority: "Medium", AssignedTo: "liu@co.com" }
]
│
▼
Apply to each (Input: value from List rows)
┌─────────────────────────────────────────────────────┐
│ For iteration 1: Current Item = {Fix printer row} │
│ → Send email to priya@co.com │
├─────────────────────────────────────────────────────┤
│ For iteration 2: Current Item = {Reset password row}│
│ → Send email to james@co.com │
├─────────────────────────────────────────────────────┤
│ For iteration 3: Current Item = {Install software} │
│ → Send email to liu@co.com │
└─────────────────────────────────────────────────────┘
Inside the Apply to Each loop, refer to the current item's fields using the "Current item" dynamic content token. Each field of the current record is available — Title, Priority, AssignedTo, and so on.
Concurrency in Apply to Each
By default, Apply to Each processes one item at a time (sequential). For large lists where order does not matter and actions are independent, enable concurrency. Click the three-dot menu on Apply to Each, choose Settings, turn on Concurrency Control, and set the degree of parallelism (up to 50). Parallel processing cuts runtime dramatically — processing 100 emails sequentially might take 5 minutes; with concurrency 10, it finishes in under 30 seconds.
Sequential (default): Item 1 → Item 2 → Item 3 → Item 4 → ...
Concurrent (degree=4): Item 1 ┐
Item 2 ├─ all running simultaneously
Item 3 │
Item 4 ┘
Item 5 ┐
Item 6 ├─ next batch starts after first batch finishes
... ┘
Do Until: Loop Until a Condition Is Met
Do Until repeats a set of actions until a condition becomes true. Use it when you do not know in advance how many iterations you need — for example, polling an external API until a job finishes, or retrying an action until it succeeds.
DO UNTIL EXAMPLE: Wait for a document to be processed
DO UNTIL: [ProcessingStatus] equals "Complete"
OR Loop count exceeds 10 (safety limit)
OR Loop timeout reaches 1 hour
Inside the loop:
ACTION: Get row (Dataverse) — fetch latest status
ACTION: Delay 30 seconds (wait before checking again)
After the loop exits:
Condition: Did it complete or did it time out?
Complete → proceed with next steps
Timed out → send failure alert
Delay and Delay Until: Pausing a Flow
The Delay action pauses the flow for a specified amount of time — minutes, hours, or days — before continuing to the next action. Delay Until pauses until a specific date and time.
Delay: Pause for 2 hours, then send a follow-up reminder
Delay Until: Pause until the contract's expiry date, then send renewal alert
Example: Send a reminder if not responded in 24 hours
ACTION: Start and wait for an approval
→ (approval pending)
ACTION: Delay — 24 hours
ACTION: Condition: Was approval completed?
NO → Send reminder to approver
YES → Continue flow
Error Handling: Run After Settings
Every action in a flow has "Run after" settings that control under which circumstances it executes. Click the three-dot menu (…) on any action and select Configure run after. You see four checkboxes:
RUN AFTER OPTIONS ☑ is successful — (default) run only if previous step succeeded ☑ has failed — run if previous step threw an error ☑ is skipped — run if previous step was skipped ☑ has timed out — run if previous step exceeded its timeout COMMON PATTERNS: Normal action: ☑ is successful only Error-handling action: ☑ has failed, ☑ has timed out → This action catches failures from the previous step Cleanup action: ☑ is successful, ☑ has failed, ☑ has timed out → This always runs, regardless of whether the previous step worked → Use for logging, notification, resource cleanup
Scope: Grouping for Try-Catch Patterns
Scope is a container that groups multiple actions. Combined with Run After settings, it creates a formal try-catch error handling pattern — just like try-catch in programming languages.
TRY-CATCH PATTERN WITH SCOPE
┌────────────────────────────────┐
│ SCOPE: Try │
│ ┌────────────────────────┐ │
│ │ Action 1: Get data │ │
│ │ Action 2: Transform │ │
│ │ Action 3: Save to DB │ │
│ └────────────────────────┘ │
└──────────────┬─────────────────┘
│
▼ (Run after: has failed OR has timed out)
┌────────────────────────────────┐
│ SCOPE: Catch │
│ ┌────────────────────────┐ │
│ │ Get error details │ │
│ │ Send alert email │ │
│ │ Log failure to list │ │
│ └────────────────────────┘ │
└────────────────────────────────┘
If Try scope succeeds → Catch scope is skipped
If Try scope fails → Catch scope runs automatically
Getting Error Details in the Catch Scope
Use the expression result('ScopeName')?['error'] to retrieve the error message from a failed scope. This gives you the exact error text to include in your alert email so developers know what went wrong without opening the flow's run history.
Expression to get error message:
result('Try')?['error']?['message']
Use in email body:
"Flow failed with error: " & result('Try')?['error']?['message']
Terminate: Stopping a Flow Deliberately
The Terminate action stops the flow immediately. Set status to Succeeded (end normally), Failed (end with error status), or Cancelled. Use Terminate with Failed in error-handling branches to mark the run as failed in Run History — making it easy to filter and find problematic runs later.
Catch Scope:
Send alert email to admin
Log error to SharePoint list
Terminate (status: Failed, message: result('Try')?['error']?['message'])
Now when you look at Run History, failed runs show a red X
with the exact error message — instead of showing as "Succeeded"
even though the core logic failed.
Filter Array: Narrowing Down a List Mid-Flow
When a "List rows" action returns many records but you only need to act on a subset, use Filter array (under Data Operations) instead of adding a filter condition to the query. This is useful when the filter condition involves complex logic or dynamic values that cannot be expressed in OData query syntax.
Filter Array example: List rows returned 50 orders. You need only orders where Amount > the variable Threshold. Filter array: From: [value from List rows] Condition: [Amount] (current item) is greater than [Threshold variable] Output: filtered array with only orders exceeding the threshold. Feed this filtered array into Apply to Each.
Select: Reshaping Data
The Select action (Data Operations) transforms each item in an array into a new shape — keeping only the fields you need and renaming them. This produces a cleaner array for HTML tables, email bodies, or external API calls.
Select example:
Input array from List rows:
{ Title, Status, Priority, CreatedBy, CreatedOn, Description, ... }
(many fields you do not need)
Select: Map only the fields for the email summary:
"Request" → [Title]
"Priority" → [Priority]
"Owner" → [CreatedBy]
"Date" → formatDateTime([CreatedOn], 'dd MMM yyyy')
Output array:
{ Request, Priority, Owner, Date }
(clean, minimal — perfect for Create HTML table)
Key Points
- Condition splits a flow into two branches based on a true/false evaluation. Use AND for all-must-match rules, OR for any-can-match rules.
- Switch handles multi-case branching cleanly when one value maps to several possible outcomes — cleaner than nested Conditions.
- Apply to Each loops through every item in a list. Enable concurrency for independent parallel processing of large arrays.
- Do Until repeats actions until a condition is satisfied — always set a safety limit (count or timeout) to prevent infinite loops.
- Delay and Delay Until pause the flow for a set duration or until a specific date — useful for reminders and expiry alerts.
- Run After settings turn any action into an error handler. Configure "has failed" and "has timed out" to catch problems from the previous step.
- Scope with Try-Catch pattern groups the main logic in a Try scope and error response in a Catch scope — the industry-standard reliability pattern for production flows.
- Filter array narrows a list mid-flow. Select reshapes array items to only the fields you need.
