The engine owns failure, plugins do not
Retries, error routing and time limits are not features of a particular plugin. The engine applies them around every node, and you control them with a reservedengine object in the node’s configuration, which the plugin never sees.
The benefit is consistency. An HTTP call, a script and a model call all retry the same way, time out the same way and route errors the same way. Plugin authors write business logic only. The cost is that the policy is per node and per message, so it is blunt: it cannot know that a 404 is final but a 503 is not unless you tell it which errors to retry.
Four things a failed message can become
When a step cannot handle a message, one of four things happens, and you choose which per node.
Retry combines with the others: a message is retried, and only when it still fails is it failed, routed or dropped. A step reporting invalid input (a bad request, not a transient fault) is never retried, because retrying cannot help.
Why all-or-nothing output matters
With retry or error routing on, whatever an attempt emitted before it failed is held back and dropped. A retry or a dead letter therefore never lands on top of a half-finished result. You pay by buffering one message’s output until its attempt ends, which is a good trade for correctness.Absorbing failure can hide a problem
Route and continue keep a flow alive. That is useful and dangerous: if a service is down, they will quietly shunt every message to a dead letter and the run will report success. A failure budget caps it. Once more than a set number of messages fail within a window, the node fails instead. Think of it as the difference between “a few bad records” and “something is broken”. Use route or continue together with a budget unless losing messages is truly fine.
How a failure spreads
A node that fails endsFAILURE. Nodes behind it end UPSTREAM_FAILED. The first terminal to fail ends the whole run and cancels the other branches.
You can change this at a join. When a node has several parents, its join rule says what a failed parent means:
This is how you build “send the report even if one optional check failed”. The consequence to keep in mind: a run can end
SUCCESS with a failed branch inside it. Look at node statuses, not only the run status.
Failure is not only errors: time
A step that hangs is a failure too. Yukta bounds every run and node with clocks that you do not have to implement. See Time limits.Design advice
- Retry the transient, route the bad data, fail the broken. Most designs want all three on different steps.
- Put side effects behind idempotence. A retried or resumed step can see a message twice. See Delivery and back-pressure.
- Give errors somewhere to go. An
errorexit with no edge drops the message. Wire a dead-letter step so a failure is visible. - Do not retry AI calls blindly. A model that returned something wrong may return it again, and each retry costs tokens. Prefer a gate after the model, or a fallback to a different model. See AI and deterministic work.
Related
- Handle failures: configure retries, routes and budgets.
- Node options reference: the
enginekeys and their defaults. - Runs: the statuses a failure produces.