> ## Documentation Index
> Fetch the complete documentation index at: https://yukta.infenia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Failure handling

> Per-node retry, error routing, failure budgets and join rules, set with the engine object in a node's configuration.

Retries, error routing and time limits are not plugin features. They are applied by the engine around every node, and you control them with a reserved `engine` object inside a node's `config`. The plugin never sees it.

<Note>
  The workflow schema is pre-beta. The `engine` option names may change before v0.1.0.
</Note>

## The engine object

```json theme={"system"}
{
  "nodeId": "classify",
  "type": "LLM_CALL",
  "config": {
    "engine": {
      "retry": { "maxAttempts": 3, "backoffMs": 200, "maxBackoffMs": 5000 },
      "onError": "ROUTE",
      "trigger": "ALL_DONE",
      "elementTimeoutMs": 20000,
      "deadlineMs": 600000,
      "failureBudget": { "maxFailures": 10, "windowMs": 60000 },
      "edgeBuffer": { "size": 100, "overflow": "DROP_OLDEST" },
      "placement": { "pool": "default" }
    }
  }
}
```

| Key | Applies to | Behaviour |
| - | - | - |
| `retry` | Processors | Re-runs the node for the failing message with exponential backoff and jitter. Every attempt counts as node activity |
| `onError` | Processors | `FAIL` (default) ends the node. `CONTINUE` drops the message. `ROUTE` sends it to the `error` port |
| `failureBudget` | Needs `CONTINUE` or `ROUTE` | When more than `maxFailures` messages fail within `windowMs`, the node fails instead of dropping or routing the next one |
| `trigger` | Nodes with several parents | Join rule; see [Nodes, edges and ports](/concepts/nodes-edges-ports) |
| `elementTimeoutMs`, `deadlineMs` | Processors, any node | Time limits; see [Time limits](/concepts/time-limits) |
| `edgeBuffer` | Any node with a processor | Drop instead of slowing producers; see [Delivery and back-pressure](/concepts/delivery-and-backpressure) |
| `placement` | Processors | Run on a remote worker pool; see [Remote workers](/operations/remote-workers) |

A malformed policy is rejected when the workflow is validated, with the node id and the option at fault. Unknown keys are rejected, `maxBackoffMs` must be below the node timeout, `retryOn` and `noRetryOn` must not share an entry, and `deadlineMs` must be above `elementTimeoutMs`.

## Retry

Retry is per message, so use it on processors that handle one message at a time. A node with `retry`, `onError` or `elementTimeoutMs` calls its plugin once per message, and the message carries an `attempt` label (`1`, `2`, ...).

* A plugin that reports invalid input (a bad request, not a transient fault) is never retried.
* `retryOn` and `noRetryOn` list error type names to include or exclude. Subclasses and causes match, and `noRetryOn` wins.
* A retried plugin can see the same message twice, so de-duplicate on the message id.

## Error routing

With `onError: ROUTE`, a message that fails all its attempts is sent to the node's `error` port, tagged with `failure.attempts`, `failure.exception` and `failure.time` (plus `origDest` and `failureReason`). Wire an edge with `"sourcePort": "error"` to a dead-letter node:

```json theme={"system"}
{ "source": "classify", "target": "dead-letter", "sourcePort": "error" }
```

An edge without a port never carries dead letters.

### All-or-nothing output

With `retry` or `onError` of `CONTINUE` or `ROUTE`, 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 partial result. The cost is that one message's output is buffered until its attempt ends.

### Failure budget

`CONTINUE` and `ROUTE` keep a flow alive by absorbing failures, which can hide a systematic problem. A failure budget caps that: once more than `maxFailures` messages fail inside `windowMs` in one execution, the node fails instead, so silent data loss cannot continue unnoticed.

## How failures propagate

| Situation | Outcome |
| - | - |
| A processor fails with `onError: FAIL` | The node ends `FAILURE`; nodes after it end `UPSTREAM_FAILED` unless a join rule lets them carry on |
| A terminal fails | The execution ends at once and the other branches are `CANCELLED` |
| A time limit expires | The node ends `TIMED_OUT` (or the attempt fails, for the element timeout, so retry and error routing apply) |
| A node's configuration cannot be resolved | That message fails like any plugin failure, so retry and error routing apply |
| A budget with a hard stop is exceeded | The whole execution ends; the failure is never retried, dropped or routed |

## Related

* [Time limits](/concepts/time-limits)
* [Routing and branching](/building/routing-and-branching)
* [Loops and retries](/building/loops-and-retries)
* [Status values](/reference/status-values)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.