跳转到内容
Developer Preview — APIs and language features may change before 1.0

Error Handling

此内容尚不支持你的语言。

Steps can fail. An HTTP request times out, an LLM returns an error, a permission is denied, a called machine throws an exception. The rule is one sentence: a machine either completes truthfully or stops loudly, unless its author wrote down what to do instead. An unhandled failure halts the machine with a legible error; on failure and fallbacks: are the two ways to continue past one. A machine never silently runs past a failed step — a failure either stops the run honestly or reaches a handler you wrote, and either way it is recorded in the behavioral ledger. The on failure block lets you catch failures and recover: provide a fallback, log the error, notify someone, or retry with a different strategy.

Place on failure at the end of a flow. If any step in that flow fails, execution jumps to the on failure block:

behaves
ask fetch_weather, from: "@mashin/actions/http/get"
url: "https://api.weather.com/current?city=${input.city}"
assuming
body: {temp: 72, conditions: "sunny"}
status: 200
compute format
{temperature: steps.fetch_weather.body.temp, source: "live"}
on failure
compute fallback
{
temperature: null,
source: "unavailable",
error: error.message
}

If the HTTP request fails (timeout, 500 error, DNS failure), the on failure block runs. The machine returns the fallback response instead of an error.

Inside on failure, the error is available through:

Reference Description
error.message Human-readable error description
error.step Name of the step that failed
error.type Error category: "timeout", "permission_denied", "runtime"
error.details Additional error-specific data

The failed step itself is also inspectable by name: after a handled failure, steps.<name>.ok is false and steps.<name>.error carries the message, code, and category — so steps after the handler can branch on what happened.

on failure
compute error_report
{
failed_step: error.step,
error_type: error.type,
message: error.message,
status: "failed"
}

A common pattern: try a primary model, fall back to a smaller one if it fails.

machine resilient_classifier
accepts
text as text, is required
responds with
category as text
model_used as text
behaves
ask primary, using: "anthropic:claude-sonnet-4-6"
with task "Classify this text.\n\nText: ${input.text}"
returns
category as text
on failure
run recover
compute result
{category: steps.primary.category, model_used: "primary"}
flows
flow recover
ask fallback, using: "anthropic:claude-haiku-4-5"
with task "Classify this text into a category.\n\nText: ${input.text}"
returns
category as text
compute recovered
{category: steps.fallback.category, model_used: "fallback"}
ensures
permissions
allowed to
model
verifies
test "primary model classifies"
assuming primary {category: "general"}
given {text: "hello"}
expect {category: "general", model_used: "primary"}

Fire off an alert when something goes wrong, then return a graceful error:

on failure
launch send_alert
machine: "@mashin/actions/notifications/send"
channel: "slack"
message: "Pipeline failed at " + error.step + ": " + error.message
compute error_response
{status: "failed", step: error.step, message: error.message}

launch is fire-and-forget: the alert is sent asynchronously and does not block the error response.

In multi-flow machines, each flow has its own on failure:

behaves
flows
flow extract
ask fetch, from: "@mashin/actions/http/get"
url: input.url
assuming
body: {}
status: 200
on failure
compute extract_error
{stage: "extract", error: error.message}
flow transform
compute process
{data: steps.fetch.body}
on failure
compute transform_error
{stage: "transform", error: error.message}

A failure in extract does not trigger transform’s error handler. Each flow manages its own recovery.

For automatic retry of transient failures, use runtime configuration:

behaves
runtime
max_retries: 3
timeout: 30000

This retries any failed step up to 3 times before triggering on failure. Use this for transient errors (network timeouts, rate limits) rather than permanent failures (invalid input, permission denied).

Both the failure and the recovery are recorded in the behavioral ledger:

  1. The original step failure (step name, error type, message)
  2. Each step in the on failure block (with normal step recording)
  3. The final output of the recovery path

This makes error-and-recovery sequences fully auditable. You can answer: “What failed? What did the machine do about it?”

If the on failure block itself fails, the machine halts with the secondary error. There is no nested error handling.

Build a machine that calls an HTTP API. Add an on failure block that returns cached data when the API is unavailable. Use error.type to distinguish between timeouts and other failures, returning different messages for each.