Node errors
When a node fails, the engine has exactly two scenarios. Either the node has an error branch wired — control goes there and the run continues. Or it has none — and then everything below on that branch is skipped with the reason “every path here died on an upstream failure”, while the parallel branches next to it play out to the end.
Turning on the error branch
- Open the node’s settings and turn on the Error output (
expose_error_output) field. - An
on_erroroutput appears on the card. - Draw an edge from it into the node that handles the failure.
Almost every step node has the field; on most of them it is off by default, on
http_request it is on. An edge leaving on_error
while the field is off is rejected by the validator as an error: a port the node
does not show means a branch that is not connected.
When the branch exists and fires, the run stays completed — the failure counts as handled. The node’s success branch is dead in that case: exactly one of its two outputs fires per run, which is why both branches may safely converge on a shared node or a shared exit.
- Execute + Data
What arrives in the error branch
The on_error edge delivers an object into the target node, on whichever input you
wired it to. You read it like any other value: {{ inputs.input.message }}.
| Field | What it holds |
|---|---|
message |
A short description of the failure for you — plus the service’s own words, when the service is yours. |
error_code |
A machine-readable code (see below), or INTERNAL when the platform could not attribute the failure to anything specific. |
exception_type |
Always an empty string. The field remains for the sake of older graphs. |
failed_node_id |
The id of the node that failed. |
failed_node_type |
The type of the node that failed. |
Branch on error_code, not on the text: the code is stable, while message is a
sentence to read and changes with our wording and the third party’s.
Error codes
The code splits every failure into two worlds, and that is what decides whether a retry happens at all.
Deterministic — the failure is a property of the request itself; retrying changes nothing and only costs money:
VALIDATION, INVALID_PATH, NOT_FOUND, ACCESS_DENIED, SIZE_LIMIT,
CONFLICT, PRECONDITION_FAILED.
Transient — the next attempt may well succeed:
PROVIDER_ERROR, CONNECTION, TIMEOUT, RATE_LIMIT.
Two codes stand apart, both about access and money, and both deterministic because
retrying does not fix them while a paid call is billed again: AUTH (the service
rejected the key itself) and BILLING (the account behind the key is out of credit
or quota).
Retries
A retry happens only when the node type declares a retry budget; how many there were is shown on the node card under Retries. Even with a non-zero budget the engine never retries deterministic codes or programming errors — those surface immediately. Network hiccups inside a model call are retried by the provider client itself, within the call.
A node also has a wall-clock ceiling — ten minutes by default. Nodes that run other nodes inside themselves (loops, sub-workflows, agents) are exempt from it: their runtime is the sum of their children’s, and every child is bounded on its own. The run-level ceiling applies either way.
Nodes without an error branch
- entry — the run’s input; there is nothing for it to fail at mid-graph.
wf_tool— workflow-as-a-tool: its failure goes either to the model or down the ordinary failed-branch path.- Tool nodes an agent calls: their failure is returned to the model as text, and the model decides what to do next.
- Configuration nodes (llm, memory, stores): they are pulled lazily, so the failure surfaces at the node that uses them.