Error handling and retries
When a command fails, the run stops with an error unless your task handles it. Use try/catch/finally to react to errors, --retry to try flaky steps again, and throw to stop on purpose when the data breaks a rule of your process. A task error handler reports and cleans up when the whole task fails, and continue on error lets single commands fail without stopping the task.
try, catch and finally
browser --action:open --url:"https://portal.example.com" --output:portal
try
capture --browser:$portal$ --action:click --dompath:"#export" --timeout:20
catch --error:err
log --message:"Export failed at line $err.CommandLine$: $err.Message$" --level:error
finally
browser --action:close --browser:$portal$
endtry
- The try section runs first. When a command in it fails, the rest of the section is skipped.
- The catch section runs when a command in the try section failed. The error counts as handled: after the block, the task goes on with the next command.
- The finally section always runs at the end: after a successful try section, after catch, after an error no catch handled (which then goes on), and when
breakorcontinueleaves the block. Close browsers, workbooks and connections here. - A try needs a catch, a finally or both, in that order.
- An error (or a
break/continue) inside finally replaces whatever came before it. A cancelled run skips finally.
In the editor, add the sections from the palette (under Try), from the + Catch and + Finally buttons on the block's end line, or from Sections in the properties panel.
The error variable
catch --error:err puts the error into a record variable named err (choose any name; in the properties panel it is the Error field). There is no built-in error variable: a catch without --error handles the error without keeping its details.
| Field | Value |
|---|---|
$err.Message$ |
The error message |
$err.Type$ |
Business or System (see below) |
$err.CommandLine$ |
Line number of the failing command, as shown in the editor |
$err.CommandType$ |
The failing command, such as capture or throw |
$err.Timestamp$ |
When the error happened |
Tasks written when every catch filled a built-in $error$ keep working: when they are opened, each of their catch sections gets --error:error.
catch can also copy the message and the line into variables of your own:
try
excel --action:open --file:C:\In\orders.xlsx --session:orders
catch --errormessage:problem --errorline:failedAt
log --message:"Line $failedAt$: $problem$" --level:error
endtry
Business and system errors
Botream distinguishes two kinds of errors:
| Type | What it means | Examples | Retried? |
|---|---|---|---|
| System | The application, the network or the machine failed. Trying again may work. | Element not found, timeout, file missing, connection refused | Yes |
| Business | A rule of your process is broken. Trying again changes nothing. | Invoice has no total, customer unknown, approval missing | No |
Every error a command raises is a system error. Errors you raise with throw are business errors unless you say otherwise. A called task's error keeps its type through runtask.
The type decides what happens next:
--retryrepeats only system errors.- In
loop --type:queue, a round that fails with a system error puts the work item back for another attempt (while the queue allows retries); a business error fails the item for good. See Work queues. - The execution page shows a failed run as Business rule or System error.
Handling one type only
catch --errortype:business (or system) handles only errors of that type. The other type passes on to an outer catch, after the finally sections in between have run.
try
runtask --name:BookInvoice --invoiceNo:$row.No$
catch --error:err --errortype:business
log --message:"Skipped invoice $row.No$: $err.Message$" --level:warning
endtry
Raising errors with throw
throw stops the normal flow with an error of your own. The nearest catch handles it; without one, the run fails with your message.
if --condition:$total$ <= 0
throw --message:"Invoice $no$ has no total"
endif
if --condition:$response.StatusCode$ = 503
throw --message:"The booking service is unavailable" --type:system
endif
--type:businessis the default; use--type:systemfor technical failures you detect yourself, so that retries and queues treat them as such.--messageis required, except inside a catch section: there,throwwithout a message passes the caught error on to the next catch outside, keeping its message and type.
try
database --action:execute --session:erp --query:"UPDATE Orders SET Status='Booked' WHERE Id=@id" --params:id=$id$
catch --error:err
log --message:"Booking failed: $err.Message$" --level:error
throw
endtry
Retrying a command
Passing problems are tried again by themselves
Commands that work with screens and files (capture, desktop, image, sap, mouse, sendkeys, browser, window, excel, csv, pdf, file, folder, zip, xml, json) try once more, after 1.5 seconds, when they fail for a reason that passes by itself:
- the page was loading or re-rendering while it was used (the element came off the page for a moment);
- a loading overlay or spinner covered the element;
- another program had the file open;
- a desktop element was being rebuilt.
The log says so ("A passing problem: … Trying once more"), and "The second try worked" when it did. Actions that must not happen twice, such as clicks, key presses, choices and uploads, are tried again only when the problem means they did not happen at all (the target was covered or gone). An element that is not found is not tried again: the command already waited its --timeout for it, and self-healing handles elements that changed. mail, restapi and database are not tried again by themselves, because their request may have reached the other side; give them --retry when repeating is safe.
--retry:0 turns the automatic try off for a command; any other --retry replaces it.
--retry
These commands accept --retry (number of extra attempts) and --retryinterval (seconds between attempts, default 1):
| Command | Typical use |
|---|---|
capture |
A web element that appears late or a page that is slow |
desktop |
A desktop element that is not ready yet |
database |
A connection or query that fails now and then |
mail |
A mail server that refuses a connection |
restapi |
A service that times out or answers with an error |
ai |
An AI model that is busy |
file --action:wait |
A file that is still being written |
runtask |
A called task that fails for a technical reason |
restapi --action:post --url:"https://api.example.com/bookings" --body:$booking$ --output:response --retry:3 --retryinterval:5
capture --browser:$shop$ --action:click --dompath:"#checkout" --retry:2 --retryinterval:2
Each failed attempt writes a warning to the log ("Attempt 1 of 4 failed: … Retrying in 5s"). When the last attempt fails too, the command fails with its error, which a catch can handle. Business errors are never retried.
Tip: Web and desktop commands already wait up to
--timeoutseconds (30 by default) for their element. Use--retryfor failures that a later attempt can cure, not to make a short timeout longer.
Retrying a group of commands
--retry repeats one command. When several commands belong together, such as signing in to a portal and opening a page, put them in a retry block (UiPath's Retry Scope): when any of them fails, the block waits and runs all of them again from the top.
retry --times:3 --interval:5
browser --action:open --url:"https://portal.example.com" --output:portal
capture --browser:$portal$ --action:click --dompath:"#login"
wait --type:element --browser:$portal$ --dompath:"#dashboard" --failontimeout:true
endretry
| Parameter | Default | Meaning |
|---|---|---|
--times |
3 | Attempts in all, the first one included |
--interval |
1 | Seconds to wait before the next attempt |
--until |
– | A condition (written like if --condition) that must be true after the commands ran; while it is false the block tries again |
retry --times:10 --interval:30 --until:"$status$ = Ready"
restapi --action:get --url:"https://api.example.com/jobs/$jobId$" --output:job
set --name:status --value:$job.Json.status$
endretry
- Each new attempt writes a warning to the log: "Attempt 2 of 3 after: …" with the error and its line.
- When the last attempt fails, the error goes on as usual: a
tryaround the block catches it, otherwise the run fails. With--until, a condition that is still false after the last attempt fails the block ("The retry condition was still false after 10 attempts"). - Business errors (
throw) are not retried, nor errors after which the action may already have happened (a mail the server may have taken) — the error goes on at once. breakandcontinueinside the block leave it at once, as dostoptaskand stopping the run.- While debugging, the run stops only at the error of the last attempt; Step into goes into the block.
- The commands inside still use their own
--retry(and the automatic second try) first.
In the editor, add Retry from the palette (Error Handling), drop the commands into it and set the attempts in its properties. The retry block needs the current agent: an older agent is not sent such a task, and the run is refused with a message to update the agent.
The task error handler
A production robot should never just stop: when something goes wrong, it should tell someone, mark the work item and close the application it was using. Instead of wrapping the whole task in a try, add one task error handler (UiPath's Global Exception Handler, Automation Anywhere's error handler). It runs only when an error that no catch handled is about to fail the task:
onerror --error:err
log --message:"Failed at line $err.CommandLine$ ($err.CommandType$): $err.Message$" --level:error
screenshot --file:"C:\Botream\Errors\$System.ExecutionId$.png" --onerror:continue
mail --action:send --smtp:smtp.company.com --port:587 --from:robot@company.com --to:support@company.com --subject:"Robot failed: $System.TaskName$" --body:$err.Message$ --onerror:continue
window --action:close --window:$erp$ --onerror:continue
endonerror
- Put it at the end of the task, on the main level. A task has one handler; it never runs in the normal flow.
--errornames the record variable that receives the error (defaulterror), with the same fields as in a catch:$err.Message$,$err.Type$,$err.CommandLine$,$err.CommandType$,$err.Timestamp$.--thensays how the run ends after the handler:fail(the default) — the run fails with the original error, as it would without a handler;complete— the error counts as handled and the run ends as Completed, with a warning in the log. Usecompleteonly for tasks that must report "handled", e.g. when the handler already put the work back.- The screenshot and the variables of the failed run are taken before the handler runs, so the execution page shows the screen as it was at the failure, not after the handler closed the application. Browsers and sessions are still open while the handler runs.
- An error inside the handler is written to the log and the run fails with the original error; the handler never runs a second time. Give its commands
--onerror:continue(see below), so that one failing clean-up step does not skip the others. - It does not run for errors a
catchhandles, for the failed rounds ofloop --type:queue(the loop handles those itself), forstoptask, or when the run is stopped or cancelled (from the web app, the tray app or because the agent shuts down). A command that times out (--timeout,wait --failontimeout:true) fails like any other command, so the handler does run. - Called tasks (
runtask): every task's own handler handles its own errors. Afterwards the error goes on to the calling task as usual (a catch there, or the caller's handler, handles it) — unless the called task's handler says--then:complete; then the called task returns normally. - Work queues: when a run ends, the server puts the item it was processing back into the queue (or fails it when it is out of retries). To fail it for good with a reason, add
queue --action:fail --item:$item$ --reason:$err.Message$to the handler. - Debugging: with Stop at errors no catch handles on, the debug run first stops at the command that failed; Continue then runs the handler, where breakpoints work as usual.
In the editor, add Task error handler from the palette (Error Handling) or with Ctrl+K: it always goes to the end of the task, wherever you drop it, and its card is tinted red with the marker Only when the task fails. Its properties are Error and Then.
Continuing after a failed command
Some commands may fail without harm: deleting a temporary file that is not there, closing a window that is already closed, sending a notification. Instead of a try block around each of them, set On error to Continue with the next command in the command's optional settings (--onerror:continue):
file --action:delete --path:"C:\Temp\export.csv" --onerror:continue --errorto:deleteError
if --condition:"$deleteError$ != null"
log --message:"Could not delete the export: $deleteError.Message$" --level:warning
endif
- The command's own retries (
--retry, and the automatic second try) happen first. When it still fails, the log gets a warning ("Line 1 (file) failed and the task goes on (--onerror:continue): …") and the task goes on with the next command. --errorto(Save the error to) keeps the error as a record, with the fields of a catch's error variable. When the command went through, the variable is empty (null), so in a loop you never see the error of an earlier round.- Stop the task (
--onerror:stop) is the default: a failure stops the task unless a catch or the task error handler handles it. - It is available on every command except blocks (if, loop, try, switch, retry, step and the task error handler) and
comment,input,output,break,continueandthrow. For a group of commands, use atryblock. - An error the command continues after is handled: the debugger does not stop at it and the task error handler does not run.
- In the blocks editor, such a command shows the marker Continues on error.
The task error handler and continue on error need the current agent: an older agent is not sent such a task, and the run is refused with a message to update the agent.
Waits that time out
wait does not fail when its time is up: it logs a warning, the task goes on, and --output receives false. Add --failontimeout:true to make it fail, so that a catch handles it.
wait --type:fileexists --path:C:\In\*.csv --timeout:300 --failontimeout:true
Messages and prompts
Cancelling a prompt dialog on the agent machine fails the command. Wrap it in try/catch when the person may cancel.
When a run fails
When an error that no catch handles ends a run, the agent keeps a snapshot before it closes browsers and sessions. The execution page shows:
- the error message with the failing line and command, and whether it was a Business rule or a System error;
- Screen when the task failed: a picture of the agent machine's screen at that moment (click for full size);
- Variables at the failure: every variable of the task, with passwords masked and long values shortened.
When the failure happens inside a called task, the snapshot is the called task's, where it actually failed. When the task has an error handler, the snapshot is taken before the handler runs.
AI failure analysis
When an AI model is set up, Analyze with AI on a failed run explains what happened, the likely cause and how to fix it, from the error, the failing command, the log and the variables (passwords are left out). The answer is kept with the run. See AI features.
Stopping at errors while debugging
In a debug run, Stop at errors no catch handles (on by default) pauses the run at the failing command, so you can look at the variables before the run fails. See Debugging.
Good practice
- Open browsers, Excel sessions and database connections just before the
tryand close them infinally, so they are closed even after an error (a close in finally that refers to something never opened fails itself). - Throw business errors for bad data and keep them out of retries; let technical failures be system errors.
- Wrap steps that fail now and then as a whole (sign in, open the page) in a
retryblock, and put atryaround it for what to do when every attempt failed. - Name the error in catch sections (
catch --error:err) and log$err.Message$and$err.CommandLine$; the line number matches the editor. - In loops over many items, put the try inside the loop so one bad item does not stop the batch, or use
loop --type:queue, which handles each item's failure for you. - Give every unattended task a task error handler that logs the error, notifies someone and closes the applications, with
--onerror:continueon its clean-up commands. - Use
--onerror:continueonly for commands whose failure really does not matter; when it does matter, handle it with a catch.