Learn how to build desktop flows that handle failures gracefully using On Block Error handlers, retry policies, and automatic recovery screenshots. This lesson takes you from brittle proof-of-concept to production-ready RPA with practical, hands-on techniques.

Picture this: you've built a desktop flow that logs into your company's legacy ERP system, extracts invoice data, and pastes it into an Excel report. It runs perfectly during testing. You schedule it to run overnight unattended, head home, and return the next morning to find it failed at step 12 — because a Windows Update dialog appeared, covered the screen, and the bot tried to click a button that was completely hidden. The error message tells you almost nothing useful, and you have no idea what the screen looked like when everything went sideways.
This is the defining challenge of Robotic Process Automation (RPA): the real world is messy, applications misbehave, networks hiccup, and pop-up dialogs appear at the worst possible moments. A desktop flow with no error handling is like a car with no seatbelt — it works great until suddenly it doesn't, and then things get very bad very fast. The difference between a brittle proof-of-concept and a production-ready bot almost always comes down to how carefully you've thought about failure.
By the end of this lesson, you'll be able to design desktop flows that handle errors gracefully, retry transient failures automatically, capture screenshots when things go wrong, and recover cleanly rather than leaving applications in a broken state.
What you'll learn:
You should be comfortable navigating Power Automate Desktop and building basic flows before working through this lesson. If you're new to the tool, start with Getting Started with Power Automate Desktop: Installing, Recording, and Running Your First Desktop Flow to get your bearings. You should also have a working understanding of variables, since error handling makes heavy use of them — Variables, Lists, and Data Tables in Power Automate Desktop: A Complete Practitioner's Guide is the right place to build that foundation.
Before diving into the mechanics of error handling, it's worth understanding why desktop flows fail so frequently compared to API-based automation. When you call a REST API, the system either returns a result or a structured error — it's deterministic. Desktop flows work by controlling a user interface, which is inherently unpredictable.
Here are the most common failure categories you'll encounter in production:
Timing failures. An application loads slowly, and your bot tries to click a button that doesn't exist yet. The action fails because the UI element isn't available at that precise moment.
State failures. Something changed in the application since you last ran the flow. A dialog appeared, a window was minimized, or a previous run crashed and left the app in a half-finished state.
Selector failures. A recent update to the application changed the underlying UI structure, so your stored selector no longer matches any element on screen. This is especially common with web applications. You can minimize this by building robust selectors — see UI Elements and Selectors in Power Automate Desktop: Building Automations That Don't Break for a thorough treatment.
Resource failures. A file doesn't exist, a network path is unavailable, or a database connection times out.
Understanding which category your failure falls into determines how you should handle it. Timing failures are often worth retrying. State failures need recovery logic. Selector failures need selector fixes, not retry loops.
In Power Automate Desktop, the On Block Error handler is a structured try/catch system — if you've written code before, you'll recognize the pattern immediately, but don't worry if you haven't, because the concept is very intuitive.
Think of it like a safety net under a trapeze artist. The performer tries to do their routine (your automation logic). If they fall (an error occurs), the net catches them (your error handler runs) rather than letting them crash to the ground (the entire flow failing with no cleanup).
To add an On Block Error block in Power Automate Desktop:
The key options in the On Block Error configuration panel are:
Continue flow run: The flow keeps executing after the error, skipping the rest of the failed block and moving to the next action after the End marker. Use this when the failed block is non-critical and you want the flow to press on.
Go to next action: Similar to continue, but specifically skips only the individual failed action and tries the next one in sequence.
Go to label: This is the most powerful option. You define a Label action elsewhere in your flow — essentially a bookmark — and when an error occurs, execution jumps directly to that label. This is how you implement recovery routines.
Stop flow: The flow halts entirely. You'd use this for critical errors where continuing would cause data corruption or other serious problems.
Key insight
On Block Error handlers nest. You can have a broad error handler around an entire workflow section, and more specific handlers around individual risky actions within it. The innermost matching handler gets triggered first, just like try/catch nesting in programming.
Let's say you're automating a legacy web portal that sometimes takes 15-30 seconds to fully load after login. Your flow might look like this conceptually:
On Block Error (Go to Label: LoginFailed)
Launch browser and navigate to portal URL
Wait for element 'Username field' to appear
Type username into Username field
Type password into Password field
Click Login button
Wait for element 'Dashboard heading' to appear
End
# ... rest of your flow continues here ...
Label: LoginFailed
Take screenshot of desktop
Log error message to file
Stop flow
If anything in that protected block fails — the browser doesn't launch, the username field never appears, the login button isn't found — execution immediately jumps to the LoginFailed label. Your recovery code runs, captures evidence, logs the problem, and stops cleanly.
Without the On Block Error handler, a failure in any of those steps would throw an unhandled exception that terminates the entire flow abruptly, leaving the browser open, no error logged, and no evidence of what went wrong.
Not every failure deserves a full error handler. Sometimes the right answer is simply: wait a moment and try again. This is what retry policies are for.
In Power Automate Desktop, most UI interaction actions — clicking elements, typing into fields, waiting for elements — have a Retry Policy tab in their properties panel. This lets you configure automatic retries at the individual action level without writing any explicit error handling code.
When you double-click an action like "Click UI Element" to open its properties:
The retry policy essentially wraps a silent try/catch around that individual action and replays it up to your specified number of times before giving up and raising the error to the enclosing On Block Error handler (or terminating the flow if there's no handler).
Tip
Retry policies are best for actions that interact with slow-loading applications, particularly web automation scenarios where page load times vary. They're not appropriate for actions that fail due to logic errors — retrying those just wastes time and hides the real problem.
There's a real cost to over-retrying. If you set an action to retry 10 times with a 30-second interval, a single failing step can stall your flow for 5 minutes before the error propagates upward. For unattended automation running overnight, this matters.
A practical rule of thumb:
| Scenario | Retry Count | Retry Interval |
|---|---|---|
| Fast local app timing | 3 | 3 seconds |
| Web application loading | 5 | 5 seconds |
| Remote desktop or Citrix | 5 | 10 seconds |
| File system operations | 3 | 5 seconds |
| Network-dependent actions | 5 | 15 seconds |
A common beginner mistake is using a Wait action (which just pauses for a fixed duration) instead of a proper retry policy. Hard-coded waits are fragile. If you add Wait 10 seconds before every click, you're optimizing for the worst case on every run — slowing down your flow even when the application loads instantly.
The better pattern: use a Wait for UI Element action (which waits until the element actually appears, up to a timeout you configure) combined with a retry policy. You wait as long as needed, but no longer, and if it still fails you retry intelligently.
Warning
Avoid sprinkling Wait 5 seconds actions throughout your flow as a substitute for proper timing and error handling. This approach makes flows slow, brittle, and hard to maintain. New engineers who inherit your flow won't understand why those waits are there, and they'll either remove them (breaking things) or keep adding more (making flows slower).
Here's a truth about unattended RPA: when a flow fails at 2 AM, you won't be there to see what happened. The error message "Element not found" tells you that a UI element couldn't be located, but not why. Was there a pop-up dialog covering the screen? Did the application crash and show an error window? Did a multi-factor authentication prompt appear?
Screenshots taken at the moment of failure answer all of these questions instantly.
In Power Automate Desktop, the Take Screenshot action lives under System in the Actions panel. It captures the entire screen (or a specific monitor in a multi-monitor setup) and saves the image to a file path you specify.
Here's the pattern for using it inside an error handler:
# At the start of your flow, define a variable for screenshot paths
Set Variable 'ScreenshotFolder' to 'C:\RPA\Logs\Screenshots'
Set Variable 'FlowRunTimestamp' to (Current datetime formatted as 'yyyyMMdd_HHmmss')
On Block Error (Go to Label: HandleError)
# ... your automation logic ...
End
# Flow continues normally after this point...
Label: HandleError
Take screenshot → Save to file:
File path: '%ScreenshotFolder%\Error_%FlowRunTimestamp%.png'
Capture: Entire screen
Write text to file:
File path: '%ScreenshotFolder%\ErrorLog_%FlowRunTimestamp%.txt'
Text: 'Flow failed at: %CurrentDateTime%'
'Last action error: %LastError%'
Stop flow
The %LastError% variable is a built-in Power Automate Desktop variable that contains the error message from the most recent failed action. Always capture it in your error handler — it's often the most useful piece of information you have.
Tip
Use a timestamp in your screenshot filename so each run creates a unique file rather than overwriting the previous one. If you're running flows on a schedule, you might have failures on consecutive nights, and you want to keep both screenshots for comparison.
Screenshots show what the screen looked like. Log files explain where in the flow you were and what went wrong. The most useful error logs combine both. Consider writing a structured text file with:
%LastError% variableFor Excel automation flows, this might mean logging which row number you were processing when the error hit — so you know exactly where to resume manual work.
For production flows, capturing a screenshot to disk is helpful, but someone needs to find and check it. A better pattern is to automatically notify the relevant person when a flow fails.
You can do this from within the error handler using the Send Email actions in Power Automate Desktop (via Outlook), or by triggering a cloud flow notification. This connects to broader patterns around error handling and retry logic in Power Automate cloud flows, which you can explore after mastering the desktop flow side.
Capturing errors is important. Cleaning up after them is critical.
When an RPA bot fails mid-process, it often leaves applications in a state they shouldn't be in:
The next time your flow runs — whether it retries automatically or a human restarts it — it needs to deal with this leftover state. Without recovery logic, you end up with flows that work perfectly the first time they run, but break on every subsequent run after a failure.
A reliable recovery routine for most flows follows this sequence:
Here's what that might look like for a flow that automates a web-based expense system:
Label: RecoveryRoutine
# Step 1: Capture evidence
Take screenshot → Save to 'C:\RPA\Logs\Screenshots\ExpenseBot_Error_%Timestamp%.png'
# Step 2: Log the error with context
Write text to file:
'Error processing expense report for employee: %CurrentEmployeeID%'
'Row being processed: %CurrentRow%'
'Error details: %LastError%'
'Timestamp: %CurrentDateTime%'
# Step 3: Close the browser (even if it's in a weird state)
Close browser (suppress errors)
# Step 4: Close Excel if it's open
Close Excel (save: No, suppress errors)
# Step 5: Flag the failed item in a tracking spreadsheet
Launch Excel
Open 'C:\RPA\ProcessingLog.xlsx'
Write 'FAILED' to cell in current row
Save and close Excel
# Step 6: Stop
Stop flow
Notice "suppress errors" in the close actions. This is critical: your recovery routine itself might encounter errors (what if the browser already crashed?). If you don't suppress errors in recovery steps, a failure inside your error handler can crash your flow in a confusing way, and you lose all the diagnostic information you were trying to capture.
Warning
Always suppress errors on cleanup actions within your recovery routine. The last thing you want is an error handler that itself throws an unhandled error. Think of it as: the recovery routine runs no matter what, it just does its best.
The gold standard for production RPA is idempotency — designing your flow so it can be safely re-run from the beginning after a failure, without creating duplicates or corrupting data.
For example, before creating an invoice in your ERP system, check whether that invoice number already exists. If it does, skip the creation step and move on. This way, a flow that fails halfway through and is restarted won't create duplicate invoices for the records it already processed successfully.
This kind of defensive design takes more thought upfront, but it dramatically reduces the cost of failures in production. It connects directly to how you structure your legacy application automation flows, where the risk of partial processing is especially high.
Real flows aren't a single linear sequence — they're often loops processing multiple records, with multiple phases (login, extract data, transform data, write data, logout). Each phase may need different error handling behavior.
A practical pattern is to use multiple On Block Error blocks with different recovery behaviors:
Key insight
Not all errors deserve the same response. Design your error handling strategy around the business impact of each failure, not just the technical category. A failed login requires immediate human intervention. A failed row in a batch of 500 might just need logging and a skip.
Here's a sketch of this tiered approach for a batch processing flow:
On Block Error (Stop flow + Screenshot)
Login to ERP system
End
For Each Row in InvoiceDataTable:
On Block Error (Go to Label: SkipThisRecord)
Extract invoice details from ERP
Transform data
Write to Excel report
End
Continue to next record:
Label: SkipThisRecord
Log 'Failed to process invoice: %CurrentInvoice%'
Set 'ErrorCount' to ErrorCount + 1
Continue loop
End For Each
Logout from ERP system
This structure ensures a bad row doesn't kill the whole batch, while a login failure stops everything immediately before wasting time attempting 500 records.
Build a desktop flow that demonstrates all three error handling concepts: On Block Error, retry policies, and recovery screenshots.
Scenario: Your flow should open Notepad, attempt to read a file that does not exist, handle the resulting error gracefully, and capture a screenshot before stopping.
Step 1: Open Power Automate Desktop and create a new flow called "Error Handling Practice."
Step 2: Add a Set Variable action. Name the variable LogFolder and set its value to C:\RPA\Practice (create this folder on your machine first).
Step 3: Add a Set Variable action for the timestamp. Use the %CurrentDateTime% system variable to build a timestamp string.
Step 4: Add an On Block Error action. In its configuration, set the error handling to "Go to Label" and type ErrorOccurred as the label name.
Step 5: Inside the On Block Error block, add a Read Text from File action. Point it to a file path that doesn't exist, such as C:\RPA\Practice\DoesNotExist.txt. In the retry policy tab, set it to retry 2 times with a 3-second interval. This simulates a transient failure scenario.
Step 6: Add an End action to close the On Block Error block.
Step 7: Add a Display Message action after the End, with the text "File read successfully — this runs if no error occurred." This confirms your flow continues normally when there's no error.
Step 8: Add a Go to action immediately after the Display Message, pointing to a label called FlowComplete. This skips the error handler when everything works.
Step 9: Add a Label action named ErrorOccurred.
Step 10: Add a Take Screenshot action. Set the file path to %LogFolder%\Error_%CurrentDateTime%.png and set capture to "Entire screen."
Step 11: Add a Write Text to File action. Write the text Error captured: %LastError% to %LogFolder%\ErrorLog.txt.
Step 12: Add a Display Message action showing "Error was handled — check log folder for details."
Step 13: Add a Label action named FlowComplete.
Step 14: Run the flow. Since the file doesn't exist, the read action will fail (after 2 retries), the handler will fire, a screenshot will be saved to your log folder, and the error message will be written to the log file.
Verification: Open C:\RPA\Practice and confirm that both an image file and a text file were created. Open the text file and confirm it contains the error message.
Mistake: Forgetting to set the label name consistently You configure On Block Error to "Go to Label: HandleError" but your Label action is named "ErrorHandler." The flow will fail with a "Label not found" error. Double-check that the label name in your On Block Error configuration exactly matches the Label action name — including capitalization.
Mistake: Placing the Label inside the protected block Your recovery label needs to be outside and after the On Block Error block it corresponds to. If you place the label inside the block, the flow will loop back into the protected section and potentially cause infinite error loops.
Mistake: Not handling errors in the error handler itself If your recovery routine tries to take a screenshot and the screenshot action fails (perhaps because of a permissions issue on the folder), you'll get an unhandled error inside your handler. Always set cleanup actions to "Continue on error" or surround them with their own simple error suppression.
Mistake: Using retry policies for logic errors A flow that fails because a selector doesn't match an element will fail on retry too — the selector is wrong, not the timing. Setting a high retry count here just makes you wait longer for the same failure. Distinguish between timing issues (retryable) and configuration issues (not retryable).
Mistake: Capturing screenshots but not the %LastError% variable
The screenshot tells you what the screen looked like. The %LastError% variable tells you what Power Automate Desktop thought went wrong. You need both. Always write %LastError% to your log file alongside the screenshot.
Mistake: One giant On Block Error block wrapping the entire flow This is better than nothing, but it means every error gets the same response — stop and screenshot — regardless of severity. Build tiered handlers that match the error response to the business impact of each flow section.
Note
If your flow runs unattended and your recovery routine tries to send an email or write to a SharePoint list, make sure those connections are already established and authenticated. Unattended flows can't prompt for credentials, so any connection that requires interactive sign-in will fail silently inside an error handler.
Error handling transforms a fragile demo into a production-ready automation. The three tools you've learned today work together as a system: On Block Error provides the structured try/catch safety net that catches failures and routes execution to recovery logic. Retry policies handle transient timing failures at the individual action level, giving unstable UI elements a few extra chances before escalating. Recovery screenshots give you the forensic evidence you need to diagnose failures that happen when nobody is watching.
The key mindset shift is moving from "how do I make this flow work?" to "how do I make this flow handle failure gracefully?" Every action that could fail should be covered by at least one of these mechanisms. For critical automation — the kind that processes payments, generates reports, or feeds downstream systems — you should assume failures will happen and design accordingly.
Here's what to focus on next:
Explore how to build robust UI selectors that reduce the rate of selector failures in the first place, so your error handlers don't get triggered unnecessarily: UI Elements and Selectors in Power Automate Desktop: Building Automations That Don't Break
Learn how to structure complex flows with subflows, which makes applying targeted error handling to specific sections much cleaner: review the overall flow architecture patterns in Desktop Flows: Automate Legacy Applications with RPA in Power Automate
If you're building both desktop and cloud flows, look at how error handling patterns translate to the cloud context: Master Error Handling and Retry Patterns in Power Automate for Bulletproof Flows
The bot that fails gracefully and tells you exactly what went wrong is infinitely more valuable than the bot that either never fails in testing or crashes silently in production. Build for failure, and you'll build something worth running.