Skip to content

Conditions and Errors

Steps run in order, top to bottom. Four tools change that: skip_if leaves a step out, conditional chooses between two lists of steps, try_catch runs a fallback when something fails, and on_error decides whether a failed step stops the run.

Add skip_if to any step. When its condition is true, the step does not run and the run history shows it as SKIPPED with the condition as the reason.

{ "name": "send_receipt", "type": "send_email",
"skip_if": "{{ is_empty(data.email) }}",
"config": { "to": "{{ data.email }}", "subject": "We got your request", "body": "…" } }

The condition syntax, and one trap with function calls, are in Variables and expressions.

A conditional step evaluates if and runs the then list when it is true, the else list when it is false.

{ "name": "route", "type": "conditional",
"config": {
"if": "{{ data.urgency == 'high' }}",
"then": [ { "name": "page_on_call", "type": "send_email", "config": { … } } ],
"else": [ { "name": "queue_ticket", "type": "http_post", "config": { … } } ]
} }

Nested steps are full steps: they can have their own skip_if, on_error and nested conditionals, and they can read outputs of the steps before them, including earlier steps in the same list. The conditional step’s output is branch (then or else) and steps_executed.

A try_catch step runs its try list. If a step in it fails, the rest of the try list is abandoned and the catch list runs instead. With catch_codes you catch only those error codes; an empty or missing list catches every error. An error not in the list passes through and fails the try_catch step itself.

{ "name": "save_copy", "type": "try_catch",
"config": {
"try": [ { "name": "upload", "type": "drive_upload_file", "config": { … } } ],
"catch": [ { "name": "tell_office", "type": "send_email", "config": { … } } ],
"catch_codes": [ "external_5xx", "timeout" ]
} }

The output is branch (try or catch) and error_code. Steps in try that finished before the failure stay done; nothing is undone.

Choose what a failed step does with on_error

Section titled “Choose what a failed step does with on_error”
ValueWhat happens
fail (default)The run stops and is marked failed, and you are notified.
continueThe failure is recorded, the step’s output becomes { "failed": true, "error": "<code>" }, and the next step runs.
retryOn a retryable error, the run is tried again later from this step; see Retries. If the retries run out, it fails like fail.

Use continue for steps that are nice to have, such as a courtesy email, and check steps.<name>.failed later if it matters. Use retry on a step that talks to another service that can be briefly down: http_get, http_post, http_request, and the Printavo and Drive steps.

The run stops at the failing step. Steps before it are not undone: an email already sent stays sent. The failing step shows FAILURE with its error.

Unless the step has on_error: retry and the error can be retried, the failure is final: the run is marked Failed with the error code and message, and FlowMint sends a failure notification. Fix the cause, then replay the run.

Some errors cannot succeed a second time and are never retried: external_4xx, auth_failed, validation_failed, config_error, permission_denied, credential_not_configured, dependency_missing, file_not_found, file_not_readable, template_not_found, invalid_input, php_error and workflow_changed. Every other error is retryable.

A step with "on_error": "retry" that fails with a retryable error is tried again automatically: after 1 minute, then 5, then 15, up to settings.max_retries times (default 3; 0 turns retries off). While it waits, the run shows Queued with the error and the failed step. If the last retry fails too, the run is marked Failed and you are notified.

A retry carries on from the step that failed. The steps before it are not run again — an email already sent is not sent twice — and the values they produced (steps.*, vars.*) are still there. The failed step itself runs again, and so does a whole conditional or try_catch that failed part-way through its own steps.

  • Don’t edit a workflow’s steps while a run waits to retry. The retry then fails with workflow_changed instead of carrying on at a step that may have moved; replay it.
  • A replay is different: it starts again from the first step.

A replay runs every step again, so it helps to know which steps repeat harmlessly:

  • Safe: drive_find_or_create_folder, drive_create_folder (returns the existing folder unless allow_duplicate is on), printavo_find_or_create_customer, fre_delete_entry, fre_delete_entries, pre_upsert_records, and every read-only step.
  • Repeats its effect: drive_upload_file, drive_create_text_file, printavo_create_customer, printavo_create_quote, http_post and http_request.
  • Email: send_email and send_email_template skip a message they already sent in the same run to the same recipient with the same subject within the hour. A replay is a new run, so it sends again.