Error Handling
Flow-level error pipelines, scoped recovery, and retry strategies.
When a block fails, the rest of the pipeline does not run. A flow recovers in a handle-errors block around the risky section, or in the flow's own error: pipeline as a last resort. Anything you don't catch ends the message as failed.
The failure model
A block error aborts the process chain at the failing block. The runtime then:
- Sets
vars.erroron the message. - Runs the nearest recovery pipeline: an enclosing
handle-errorsblock'serrorchain, or the root flow'serror:chain. - If the recovery pipeline succeeds, its output becomes the result and the flow completes normally. With no recovery pipeline (or if it fails too), the message ends as failed.
Both recovery pipelines see the failure as a structured variable:
| Field | Contains |
|---|---|
vars.error.message | The failing block's error string. |
vars.error.flow | The enclosing flow (or handle-errors block) name. |
vars.error.block | The failing block's label; empty when the error did not originate in a leaf block. |
Flow-level recovery: the error: pipeline
A root flow may declare an error: chain as a sibling of process. It is the 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 rest call fails and nothing caught it inline, so the error pipeline sets a 502 status and returns the error detail to the caller.
Only root flows may declare error:. Sub-flows nested inside composites cannot; wrap the risky blocks in a handle-errors block instead.
Scoped recovery: handle-errors
handle-errors is a composite with two slots, a process chain and an error chain. If any block in process fails, error runs with vars.error set, and when it succeeds the flow continues normally after 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 charge call fails but the flow completes with HTTP 200. handle-errors blocks nest, inside composites and each other, and each sets its own vars.error when its section fails.
Validation rejections
A validate block is not an error path. When a CEL rule fails it rejects the message: the flow stops and a response is returned, but no error pipeline runs. The failing rules' messages land in vars.validationErrors, the response is HTTP 422 by default (override with rejectStatus), and an onReject sub-flow can shape the body.
HTTP behavior
For flows fronted by an http source, the outcome maps to a status:
| Outcome | Status |
|---|---|
| Completed | 200, or vars.httpStatus when set (100 to 599) |
| Dropped | 204 No Content |
| Failed (uncaught error) | 500 with {"error": ...} |
| Timed out | 504 Gateway Timeout |
A recovered flow is a completed flow. Set vars.httpStatus in the recovery pipeline when the caller should still see an error code, as in the 502 example above.
Retries
Two retry mechanisms ship with the runtime. Rate-limit retries live on the http-client connector: when an upstream returns 429 Too Many Requests, the rest block re-attempts automatically, honoring the Retry-After header and otherwise backing off exponentially.
connectors:
- name: payments
type: http-client
settings:
baseURL: https://api.payments.example
retry:
maxAttempts: 5 # total attempts including the first (default 3)
maxBackoff: 30s # cap on each wait (default 30s)LLM self-healing is the ai-retry composite: it runs a protected process chain and, on failure, lets a model inspect vars.error and the message, revise it, and re-run the chain up to maxAttempts before falling through to its error chain. See ai-retry.