Error handling
What happens when a step fails: the default stop, per-step on-failure strategies (retry, continue, retry-then-continue), and the error port pattern.
By default a failing step fails the run — loud and immediate, which is what you want while building. Production workflows usually want something gentler on the steps that talk to the outside world.
Per-step strategies
Steps that can fail (web request, AI, connected apps) have an On failure setting:
| strategy | behavior |
|---|---|
fail (default) |
the step and the run fail; the run detail shows where and why |
retry |
re-run the step up to 5 times with a backoff you set; exhausting retries fails the run |
continue |
don't fail — route a {ok: false, error: …} payload out the step's error port |
retry_then_continue |
retry first; if still failing, continue out the error port |
The error port pattern
With continue, the step's error port becomes a wired lane:
web request ─ ok → normal path
└ error → set fields (context) → send email (alert)
The error payload carries ok: false and the failure reason —
branch on it, log it to a store, or
alert someone. The run completes (that's the point), so watch the
error lane, not the run status, for these failures.
Reading failures
A failed run shows the failing step in red on its
step timeline — with the exact input it received,
the error, and an attempt N chip when retries ran. Repair with
AI on the failing step opens the canvas with the failure loaded
so Max can fix the config and re-verify.
Timeouts and stuck runs
- Web requests have a per-step timeout (default 30s).
- A run whose job dies out from under it (deploy, crash) doesn't hang forever — a sweeper marks it failed with an "orphaned" error rather than leaving it running.
Gotchas
continuerequires wiring the error port — an unwired error lane means failures route into nothing and vanish. The canvas flags this.- Retries re-run the whole step — keep retried steps idempotent (GETs, upserts) and put non-idempotent sends after the shakiest step, not before it.