Skip to main content
Version: 5.1

Error Handling and Review Activities

Failures and risky steps are where durable workflows earn their keep. Instead of scattering try/catch blocks and retry loops through your flow, you attach a retry policy to each activity call — and for the steps that matter most, you put a human review in front of the step or behind its failure.

Retry policies​

Every activity call takes a Retry Policy, which says what the workflow should do after a failed attempt to execute that activity.

PolicyWhat happens on failure
No Retry (default)The activity call returns the error from its first failed attempt, and with Check Error selected the workflow fails with it.
Auto RetryThe engine re-executes the activity with configurable attempts, delay, and backoff.
Human ReviewA review task is created for the roles you name. The reviewer can retry as-is, retry with corrected input, or fail the step.
Retry, then ReviewThe engine retries first, as with Auto Retry, and raises the review only when the last attempt fails. Max Retries is required here.
The activity call form with the Retry Policy dropdown open on its optionsThe activity call form with the Retry Policy dropdown open on its options

Auto retry — for transient failures​

Choosing Auto Retry adds the backoff fields to the form. Every one of them is optional, and a field left empty falls back to its default.

FieldRequiredDescription
Max RetriesNoMaximum retry attempts. Defaults to 3.
Retry DelayNoInitial delay in seconds before the first retry. Defaults to 1.0.
Retry BackoffNoMultiplier applied to the delay after each retry. Defaults to 2.0.
Max Retry DelayNoCap on the delay between retries, in seconds. Left empty, the delay keeps growing by the multiplier.
The activity call form with Auto Retry chosen, showing Max Retries, Retry Delay, Retry Backoff, and Max Retry DelayThe activity call form with Auto Retry chosen, showing Max Retries, Retry Delay, Retry Backoff, and Max Retry Delay

Human review — when a person should fix it​

Choosing Human Review hands a failure to a person instead of to the engine. The workflow does not fail along with the activity, and the engine does not retry on its own: the run parks at that step, and a review task is raised carrying the failing input and the error it produced. The task takes its name from the activity being called, so there is nothing to name in the form.

The review is listed on the Human Tasks page of the Control Plane for the roles you name below, matched by exact role name. Until one of them decides it, the run waits there durably and holds no threads or connections, the same as any other durable wait. The decision is what resumes it.

FieldRequiredDescription
Reviewer RolesYesOne or more roles permitted to decide the review, for example Finance. It can be left empty only when Reviewers names the users who may decide; the form refuses a review that names nobody.
Title, Description, TimeoutNoHow the review reads in the inbox and how long it may wait. Left empty, the title and description are worded from the activity and its failure, and the review waits indefinitely.
The activity call form with Human Review chosen, showing the Reviewer Roles field set to FinanceThe activity call form with Human Review chosen, showing the Reviewer Roles field set to Finance

Approval gates — review before the step runs​

Some steps should never run without sign-off, even when nothing has failed. A gate like that is the Approval Policy of the activity call. It is on the Call Activity form of a workflow, and on the form where a durable agent registers the activity as one of its capabilities.

FieldRequiredDescription
Approval PolicyYesNo Approval (the default) runs the activity unattended. Human Approval gates it behind an approval review.
Reviewer RolesYesShown for Human Approval. One or more roles permitted to decide the approval, for example Finance. It can be left empty only when Reviewers names the users who may decide.
The activity call form with Approval Policy set to Human Approval and Reviewer Roles set to FinanceThe activity call form with Approval Policy set to Human Approval and Reviewer Roles set to Finance

With the gate on, the run suspends durably before every call to that activity and raises a review activity showing the arguments it proposes. The reviewer proceeds, proceeds with edited arguments, or rejects. Gates are decided on the Human Tasks page of the Control Plane. See Decide a review activity.

Error handling in the workflow logic​

Retry policies handle the step; your workflow logic handles the outcome. Clearing Check Error on an activity call hands the failure back to the workflow as a value instead of propagating it out of the workflow, and from there you branch on it like on any other value.

A workflow diagram where sendConfirmationEmail feeds an If condition on emailResult is error, which routes to notifyFailedEmail before rejoining startShipmentA workflow diagram where sendConfirmationEmail feeds an If condition on emailResult is error, which routes to notifyFailedEmail before rejoining startShipment

To handle an activity failure inside the workflow, as in the diagram above:

  1. On the workflow diagram, click the activity call whose failure you want to handle, sendConfirmationEmail here.

  2. Expand Advanced Configurations and clear Check Error. A Result field appears.

  3. Name the Result variable, for example emailResult, and click Save. The variable holds the error the activity failed with, or nil when it succeeded, and the node now shows the variable under the activity name.

    The activity call's Advanced Configurations with Check Error cleared and the Result variable named emailResultThe activity call's Advanced Configurations with Check Error cleared and the Result variable named emailResult
  4. Click + below the activity and, in the node panel under Control, click If.

  5. In Condition, write the error check, emailResult is error. Selecting the variable under Variables in the expression helper saves typing it. Click Save.

  6. Click + on the branch taken when the condition holds and add the steps that deal with the failure, here a Call Activity step calling notifyFailedEmail. Click Save.

Clearing Check Error on an activity call, naming its result variable, and branching on emailResult is error to call notifyFailedEmailClearing Check Error on an activity call, naming its result variable, and branching on emailResult is error to call notifyFailedEmail

Both branches rejoin the flow after the If, so startShipment runs whether or not the email failed. Leave the else branch empty when there is nothing to do on success.

Choosing a policy​

SituationPolicy
Flaky downstream, safe to repeatAuto Retry
Bad input a person could correctHuman Review
Flaky downstream that may still need a personRetry, then Review
Risky or irreversible step (payments, deletions)Approval gate (Approval Policy set to Human Approval)
Business-level failure with a fallback pathNo Retry + workflow logic

Crash recovery​

Everything above rests on one guarantee: the engine writes down the outcome of every step as that step completes. When a run resumes after a crash, a restart, or a redeploy, the workflow function replays, and each step that already finished is read back from the record instead of being run again.

StepWhat is read back on replay
An activity callThe value the activity returned, so the card is not charged and the email is not sent a second time.
A data event waitThe value that was delivered, so the run does not wait for it again.
A human task waitThe decision the person submitted.
A durable timerThe original deadline, so an elapsed wait does not restart its clock.
Current timeThe instant the run first reached that step. The workflow always works with the time at which it first got there.

This is why a durable workflow needs no state management of its own. You write no checkpoint rows, no status columns, and no resume logic, and you do not reload progress when a run picks back up: the variables in the workflow body are rebuilt from the record, so the code after a wait sees exactly what the code before it left behind. The recorded events for a run are listed in its Event History in the Control Plane, which opens from the debug-information icon on the execution's Execution card.

What this asks of you in return is a deterministic workflow body, since that is the part that runs again on every replay. See Activities.

Next steps​