*Workflows › Run*

# Errors and retries

> Two different things go wrong in a run: a step reports a problem, or the engine itself breaks. They are treated differently, and the difference is the most important thing on this page.

**When something goes wrong**
- A step fails, HTTP 404 or 500, a value of the wrong type, a missing value, a permission refused → The run ends as `failed` at once. The failed step is shown with its error. (No automatic retry. Fix the cause, then press «Retry». Inside a Try step, the run goes on after the Try instead.)
- The engine breaks, an unexpected error, a database blip → The run waits 30 seconds, then 60 seconds, and tries again, up to three attempts in all. (Only the failed work is repeated; finished steps are replayed from the journal.)
- The definition cannot run, invalid version, a limit exceeded, a step this build does not know → The run ends as `failed` with no retry: the next attempt would fail the same way. (Retrying by hand reproduces the same error.)
- A worker dies mid-run → After the lease expires (300 seconds) the next minute's tick takes the run over. A takeover costs one attempt. (After three attempts it ends as failed.)

## A step fails: the run fails

When a step reports an error, such as an HTTP 404, a permission refused, a value of the wrong type, a missing value or a limit that the step exceeds, the run ends as failed at once. There is no retry of the step and no option on the step to continue. The one way to carry on after a failure is to put the step inside a «Try» step; see [Keep going after a failure](https://docs.filmo.io/workflows/errors-and-retries#try).

- A failed step is not charged.
- The engine does not retry on a 500 from your server, or on a timeout. Fix the cause, then press Retry on the run.
- The error is stored at the step, so the run viewer shows exactly which step stopped and why.

## Keep going after a failure: the Try step

The «Try» step ("If a step fails, continue") has no fields and one body, like a loop. It runs the steps inside it. If one of them fails, the steps after it inside the body are skipped and the run goes on with the step that follows the Try, instead of ending as failed.

### What it publishes

A Try publishes only its outcome, as «Try result». The variables the steps inside created do not come out: after a failure some of them may not exist.

| Field | Type | Value |
| --- | --- | --- |
| «Has failed» (`failed`) | True or false | False if every step inside completed, true if one failed. |
| «Error message» (`errorMessage`) | Text | The message of the step that failed. Empty when nothing failed, so you can always compare it as text. |

### What it catches

- It catches failures of steps: an HTTP response that is not 2xx, the error an action returns, an invalid value and an exhausted credit. A loop that exceeds its laps is also a step failure.
- It does not catch what is not a step failure. A break of the engine (retried as described in the next section), a fatal error (listed after it) and a Jump pass through it, and so does a wait: a wait inside a Try parks the run as usual.
- Nested Try steps: the innermost one catches the failure.
- The Try step itself takes no credit and no position in the run. The steps inside it count as usual; a step that failed is not charged.
- On «Custom development», the message of a failure inside a Try is the generic one.

### The pattern: Try, then a condition on failed

1. **Wrap the steps that may fail** Add a «Try» step and drop into it the steps that can fail, such as an HTTP request to a system that is sometimes down.
2. **Test the outcome** After the Try, add a «Condition» on «Try result» «Has failed» (is equal to true).
3. **Say what to do** In the branch for a failure, notify someone, create a task or take another route, using the error message in the text. In the other branch, carry on.

- If you do not test the outcome, the failure is silent: the run goes on and no one is told. Put the check after the Try.
- A run in which every failure was caught ends as completed, and no failure notice is sent.
- In the run viewer the step that failed inside the Try is marked as a caught error (muted red, dashed). The Try's own panel shows the message, or "No errors".
- When a run resumes after a wait, a caught failure is repeated at the same place from the record: the step that failed is not run again.

> **Note:** Pressing Retry on a failed run never re-runs a caught failure: only failures that ended the run are run again.

## The engine breaks: it retries

If the engine itself throws an error (an unexpected exception, a database problem), the run is not failed at once. It waits, and tries again, three attempts in all:

| Attempt | If it fails | Wait before the next |
| --- | --- | --- |
| 1st | State «Waiting» | 30 seconds |
| 2nd | State «Waiting» | 60 seconds |
| 3rd | State «Failed» | None: it is final |

- The waits are minimums: the run is woken by the next minute tick, so each can be up to about a minute longer.
- Only the failed work is repeated. Completed steps are replayed from the record and cost nothing.
- A wait step is not a failed attempt and does not use one. A workflow with three waits does not run out of attempts.
- A worker that dies mid-run is detected when its five-minute lease expires. The takeover counts as an attempt. If the run was already on its third, it ends as failed with "the run died 3 times without completing; it is not retried again".

## Errors the engine never retries

Some errors would fail the same way every time, and would only repeat a step's effects on the way, so they end the run as failed immediately:

| Code | Message (translated) | Cause |
| --- | --- | --- |
| `invalid_definition` | The frozen version is not a valid definition | A version published before validation became stricter, or one the engine cannot read. |
| `unknown_action`, `unknown_step_type` | The definition uses an action or a step this build does not know | The workflow was saved by a newer or older version of the engine. |
| `jump_target_not_found` | The jump points to a step that is not in scope | A Jump whose target no longer exists. |
| `task_budget_exceeded` | The run exceeded the budget of 1000 steps | Too many steps in one run, usually a loop. |
| `replay_too_large` | The run's record has more than 1000 steps; it cannot be resumed | A run that grew too big to resume after a wait. |
| `feeder_collection_too_large` | The iteration resolved more than 500 elements | An Iterate step over a collection that is too large. Nothing is processed. |

The loop limit is a step failure, not an engine error: it ends the run as failed (unless the loop is inside a Try) with "the loop exceeded the N laps allowed", and Retry reproduces it.

## Reading an error

- On the run card, a failed run shows the error message in red. The error code is not shown there. If there is no message, the card says "Something went wrong during the run".
- Click the failed step. Its panel opens on the «Error» tab with, when they exist: «HTTP status» (for HTTP), «Response» (the body), «Fields» (which field and why), «Details» and «Code».
- A step that failed because a tax is not mapped in an accounting package shows a button, «Assign the tax in {{provider}}», that opens the map. After you save it, the app offers to retry the runs that were blocked.
- The messages the engine writes are in Spanish whatever the language of the app. The messages in these docs are translations.

### The error codes of capability steps

| Code | Meaning |
| --- | --- |
| `forbidden` | The creator of the workflow does not have the permission, or the action is locked by a plan, an add-on, a provider that is not connected, or the AI credits. The message names the action. |
| `invalid_input` | The inputs do not satisfy the action, with a line for each field that fails. |
| `not_found` | The action does not exist, or the record it works on does not. |
| `execution_error` | The action itself failed; the message is the one it produced. |

## Retry by hand

Press «Retry» on a failed run. It uses the same run, resets its attempts to zero, deletes only the failed records, and starts again: completed steps are replayed, and the failed step is executed live. It runs the version the run started with, so editing the workflow does not change it. It does not check your credit before it queues; if there is none, the run pauses at the step that needs it. There is no way to supply a different input.

## Repeats and the side effects of steps

A run does not repeat finished steps. But if a worker dies after a step's effect happened and before Filmo recorded that the step finished (a very short window), the step runs again. Filmo narrows that window in three layers, and does not claim to close it:

| Layer | Protects against |
| --- | --- |
| The run key | Two runs for one fact: the same webhook delivery, the same event, the same schedule occurrence. |
| The run's record | Redoing work already done inside a run. |
| A key per step | Doing an effect twice across attempts. HTTP requests other than GET carry it as `Idempotency-Key`; the email sent from Filmo and calendar events pass a key to the provider. Other writes, mailbox sends and sends to accounting packages included, are guarded by Filmo for 24 hours and, for accounting, by the link between the Filmo document and the one in the package. Voiding in Xero uses a key of its own per link. |

> **At least once:** Treat the effects of a step as at least once, not exactly once. Make the receiving end ignore a repeat when it matters, using the Idempotency-Key header the HTTP step sends.

## Who is told when a run fails

In the workflow's settings, «Notify me if it fails» offers «In-app notification and email» (the default), «In-app notification only», «Email only» and «Don't notify».

- The people told are the workflow's creator, if they still have access to the company, otherwise all the administrators (owners and administrators; the Filmo support role is never told). Never the person who pressed the button.
- An in-app notice goes out on every failure, with the workflow name and the start of the error (cut at 200 characters), linking to the run.
- An email goes out at most once an hour per workflow, whichever failure comes first. The email names the workflow and links to the run; it does not carry the reason. The hour is spent even if the email then fails to send.
- A pause, a cancel and a failed attempt that will be retried do not send anything. The notice is sent when the run has ended as failed. A run whose failures were all caught by a Try ends as completed and sends none.
- Because the email is limited to one an hour, a burst of failures produces one email and many in-app notices.

> **Tip:** Pair a workflow that must not fail silently with the in-app notice, which is not rate-limited, and with a quick look at the run.

---

Canonical URL: https://docs.filmo.io/workflows/errors-and-retries  
Last updated: 2026-10-09