Guide

Error handling

When a block fails, a flow has two ways to recover: wrap a section in a handle-errors block for inline recovery, or give the whole flow a sibling error: chain as a last resort. Both run a recovery chain that sees the failure as vars.error. Anything you don't catch ends the message as failed.

Open the error-handling sample ↗

Inline recovery

The handle-errors block

A composite with two slots: a process chain and an error chain. The process chain runs; if any block in it fails, the error chain runs instead, with vars.error set. If the error chain succeeds, the flow continues normally — the failure is recovered. The error chain sees the message as it entered the block.

- type: handle-errors
  name: charge
  process:
    - type: rest
      name: call-charge
      settings:
        connector: payments
        method: POST
        path: /charges
        body: '{"amount": body.amount}'
  error:
    # runs only if the process chain failed
    - type: set-payload
      settings:
        value: '{"status": "degraded", "reason": vars.error.message}'

Here the upstream charge call fails. Because the error chain succeeds, the message recovers and the flow completes normally (an HTTP source would return 200 with the degraded body).

Nesting. handle-errors can nest inside other composites and inside other handle-errors blocks. Each one sets its own vars.error when its process chain fails.
Last-resort recovery

Flow-level error: chain

A root flow may declare an error: chain as a sibling of process. If the process chain fails and nothing caught it, the message is handed to the error chain with vars.error set; its output becomes the flow's result. It's the difference between a per-section try/catch (handle-errors) and a whole-flow safety net.

flows:
  - name: charge-flowlevel
    source: { connector: api, type: http, settings: { path: /flowlevel } }
    process:
      - type: rest
        name: call-charge
        settings: { connector: payments, method: POST, path: /charges, body: '{"amount": body.amount}' }
    error:
      - type: set-variable
        settings: { name: httpStatus, value: "502" }
      - type: set-payload
        settings:
          value: >
            {
              "error":       vars.error.message,
              "failedBlock": vars.error.block,
              "flow":        vars.error.flow
            }

The call fails, nothing caught it, so the error chain runs: it sets a 502 status and returns the error detail to the caller.

Root only. Only top-level flows may declare error:. Sub-flows inside composites must not — wrap them in a handle-errors block instead.
Recovery context

vars.error

Both recovery chains receive a structured error on the message:

FieldContains
vars.error.messageThe failing block's error string.
vars.error.flowThe enclosing flow (or handle-errors block) name.
vars.error.blockThe failing block's label; empty if it didn't originate in a leaf block.
HTTP

Status codes & uncaught errors

For flows fronted by an http source, set vars.httpStatus (100–599) to control the response code. If you never set it, the status follows the flow outcome.

Set a status

- type: set-variable
  settings: { name: httpStatus, value: "502" }

Read just before the response is written; an out-of-range or unset value falls back to the outcome default.

Outcome → status

completed200 (or your vars.httpStatus)
dropped204 No Content
failed (uncaught)500 with {"error": …}
timed out504 Gateway Timeout
Uncaught errors. If neither a handle-errors block nor a flow error: chain catches the failure, the message ends as failed — a terminal event on the flow-event bus — and an HTTP source returns 500.
Self-healing

ai-retry

For failures an LLM can fix, ai-retry runs a protected process chain and, on error, lets the model inspect vars.error and the message, revise the body, and re-run the chain up to maxAttempts. When attempts are exhausted it falls through to the optional error chain. It pairs naturally with ai-mapping, which errors on schema-validation failure.

- type: ai-retry
  name: resilient-charge
  connector: claude
  maxAttempts: 3
  prompt: >
    Inspect vars.error and the body, correct it (e.g. fix a malformed amount),
    and produce a revised message to retry.
  process:
    - type: ai-mapping
      settings: { connector: claude, prompt: "Build a charge request.", outputSchema: "…" }
  error:
    - type: set-payload
      settings: { value: '{"status": "degraded", "reason": vars.error.message}' }