- Documentation
- Workflows
- Recover from a failed step
Recover from a failed step
Use a node's On Error policy to keep a run alive when a step fails: continue with a fallback, or route the error to a recovery branch.
What On Error does
Every node has an On Error policy that decides what happens when that step fails after its retries are exhausted. You set it in the node inspector, under the On Error section, on any node that does work and can fail (app actions, HTTP, code, transforms, AI nodes, Call Workflow, and the data nodes).
There are three modes:
| Mode | What happens | Run survives? |
|---|---|---|
| Stop the run | Fail the run on error. This is the default. | No |
| Continue | Keep going with your fallback output (or empty data); the error is on meta.error. | Yes |
| Route to recovery branch | Send errors down a separate branch you build. | Yes |
On Error is the inline, same-run reaction to a failure. It runs at the step itself, so a node can fail and the run keeps moving. That is different from the run-level error handler trigger, which fires only after the whole run has already died.
If a node has retries configured (for example, an HTTP node retrying on a 502), those run first. On Error only decides what to do once the last retry has failed.
Continue past a failure
Pick Continue when a step is non-essential and you would rather skip it than abort the run. A failed enrichment lookup, an optional notification, a best-effort write: continue keeps the run alive and hands the next step your fallback output (or an empty object) instead of a result.
Open the node inspector
Select the node that might fail and open its configuration panel.
Set On Error to Continue
In the On Error section, choose Continue. The run will now proceed down the node's normal connection even when the step fails.
Add a fallback output (optional)
Continue reveals a Fallback output field. Enter a static JSON object to substitute as the step's
datawhen it fails, so downstream mappings see a stable shape instead of an empty object. Leave it empty to pass{}.
When the step fails, its output is:
{
"data": {
/* your fallback output, or {} */
},
"meta": {
"errorHandled": "continue",
"error": {
"code": "UPSTREAM_REQUEST_FAILED",
"classCode": "ADAPTER_EXECUTION_FAILED",
"providerStatus": 502,
"message": "An external service is temporarily unavailable. Please try again later.",
"detail": "Adapter execution failed for \"Slack: Send Message\": Bad gateway",
"retryable": true,
"failedStepId": "s_action_3",
"failedNodeId": "node_action"
}
}
}That is an app action whose provider answered 502 with no matching entry in the app's error contract, so the code falls back to UPSTREAM_REQUEST_FAILED. Note message is TaskJuice's own plain-language summary — the provider's own words ride at the end of detail.
The error detail always rides on meta.error, so a recovered failure is never silently lost. The next step can read it and branch on it, from the failed step itself (here lookup_1 is the failed step's node ID):
$steps.lookup_1.meta.error.code
$steps.lookup_1.meta.error.message$error is not bound after a Continue. It is bound only on a Route recovery branch (see below).
Route a failure to a recovery branch
Pick Route to recovery branch when a failure needs its own handling: log it, repair the payload, send it to a dead-letter step, or retry through a different path. Route gives the node a second output handle you wire to a separate branch.
Set On Error to Route to recovery branch
In the On Error section, choose Route to recovery branch. The node gains a second output handle on the canvas, labeled On error with a dashed connector, so the recovery path is visually distinct from the success path.
Wire the error handle to your recovery steps
Drag from the On error handle to the first step of your recovery branch. Until you do, the inspector shows a reminder, and publishing fails with a validation error rather than silently falling back to Stop.
Filter by error code (optional)
Add one or more error codes to route only those failures down the branch. Codes outside the list fall through to Stop. Leave the list empty to route every error.
A failure carries two codes and matches on either one:
code, the specific code the app reported (AUTH_EXPIRED,NOT_FOUND,RATE_LIMITED), andclassCode, the broader kind of failure it was (ADAPTER_EXECUTION_FAILED). List the specific code to route one kind of failure; list the class code to catch every failure of that kind. Read either off a failed run's error panel — see read a failed run.
In the recovery branch, the failing step's input is forwarded as data so you can inspect or repair the original payload, and the structured error is available on the $error expression binding:
$error.code
$error.classCode
$error.providerStatus
$error.message
$error.itemSee Expressions for how to read $error in your recovery steps.
Which item failed
When the failing step runs inside a Loop, the error carries the exact item that failed (item) and its position in the loop (iterationIndex). The position is zero-based: it is the same number $currentIndex gives inside the loop, so the first item is 0. This holds for parallel Loops too. When Loops are nested, both fields describe the innermost Loop around the failing step. In this example the fifth item failed:
{
"code": "RATE_LIMITED",
"classCode": "ADAPTER_EXECUTION_FAILED",
"providerStatus": 429,
"message": "Too many requests. Please try again later.",
"failedStepId": "s_action_3",
"failedNodeId": "node_action",
"iterationIndex": 4,
"item": { "id": "cus_8842", "email": "ada@example.com" }
}So a Loop over a thousand records can route the four that failed to a cleanup branch, with full context on each, while the rest of the batch completes.
Call Workflow failures
A Call Workflow step runs another workflow, and that workflow can fail. What On Error catches depends on the step's Run mode:
| Run mode | When On Error applies |
|---|---|
| Wait for completion | When the called workflow fails (error code CHILD_WORKFLOW_FAILED) or does not finish within Maximum wait (error code SUBFLOW_TIMEOUT), and when the step cannot start the called workflow. |
| Start and continue | Only when the step cannot start the called workflow. The step does not wait, so a later failure of the called workflow never reaches it. |
| Run once per item | Only when the step cannot start the runs. A failed item never fails the step: it completes with each item's status in items, so check those statuses in a later step. |
For a failed called workflow, $error.detail names the called workflow's run and what it reported, and the step's meta.childWorkflowRunId still links to that run. Inside a Loop, $error.iterationIndex and $error.item tell you which item's call failed, exactly as for any other step.
To route only timeouts and let other failures stop the run, choose Route to recovery branch and add SUBFLOW_TIMEOUT to its error codes.
A single-step test of a Call Workflow step does not wait for the called workflow to finish, so it shows On Error only for a step that cannot start the called workflow. To see a failure or timeout of the called workflow handled, run the whole workflow.
Test it before you publish
On Error resolves at the step itself, which means a single-node test run exercises it the same way a live run does. Run the node in test mode with a payload that makes it fail, and you see the same meta.error block and meta.errorHandled stamp the production run produces. You do not have to publish and wait for a real failure to know your error handling works.
See a recovered step in run history
A node that errored but was handled inline is marked as recovered everywhere the run page sums up its steps:
- The step row shows an amber Recovered icon instead of the green tick.
- A Recovered N filter sits beside Problems (shown only when a step recovered) and lists the recovered steps. Recovered steps do not count as Problems: you chose that outcome when you set On Error, so they need no action.
- The progress bar draws each recovered step as an amber segment, and its caption counts them ("5 worked · 2 recovered").
- The run summary adds how many steps hit an error that On Error handled.
- The step's details read Recovered · Continue or Recovered · Route.
The run itself stays Completed, so a recovered failure is easy to tell apart from a failed step that stopped the run.
Choose the right tier
On Error is the middle of three ways to react to a failure. Pick by how much power you need and how fast you need it:
| You want to... | Use | Why |
|---|---|---|
| Recover this node and keep the run going | On Error (Continue or Route) | Inline, same run, no extra workflow. The run never dies. |
| React after the whole run has failed | Error handler trigger | Heavyweight, per-run, full action library. Fires on the terminal failure. |
| Observe a node without changing the flow | Lifecycle hook | Cheap per-node side-effect (audit, cost-warn). Never alters routing. |
The key boundary: Continue and Route keep the run alive and successful, so no run-level error workflow fires. Only Stop (or an unhandled error inside a recovery branch) lets the failure reach the run-level error handler. If you set a node to Continue, your error workflow will not trigger for that node — that is the point.
When Continue and Route are unavailable
Your account or workspace can have a policy that holds On Error at Stop, for regulated or compliance-sensitive work where a failure must never be swallowed. When it applies, Continue and Route are disabled in the inspector with a note saying so, and no step can loosen it. This policy is not a setting in the app; contact support to have it set or removed.
Next steps
- Write expressions to read
$errorin a recovery branch. - Route and repeat steps to build the branch a failure routes into.
- Read a failed run to see recovered steps in run history.