--- title: 'Retry' description: 'Re-run a wrapped step up to N times with exponential backoff.' --- # Retry Wraps a single downstream node and retries it on failure up to `maxAttempts` times, with exponential backoff between attempts. After a successful attempt the **success** branch fires; if all attempts fail, the **exhausted** branch fires. ## When to use - Calling an API that occasionally returns transient errors (429, 503, 502). Retry absorbs the blip transparently. - Wrapping a flaky integration whose failures are rarely real errors. - Any "try again before giving up" pattern. ## Configuration | Field | Required | What it does | |---|---|---| | `maxAttempts` | Yes | Number of total tries (1-10). Default 3. | | `backoffMs` | Yes | Initial delay before the second attempt (100-60000 ms). Default 1000. | | `backoffMultiplier` | No | Multiplier applied to the backoff on each retry. `1` = linear, `2` = doubling. Default 2. | With default settings: - Attempt 1 at t=0 - Attempt 2 at t=1000ms (after 1s backoff) - Attempt 3 at t=3000ms (after 2s backoff) ## Handles - **Wrapped** — the single downstream node to retry. Wire exactly one node here. - **Success** — fires after a successful attempt. Downstream nodes can read the wrapped node's output as normal. - **Exhausted** — fires after `maxAttempts` failures. Wire this to a notification or a fallback path. ## What it outputs ``` { attempts: 2, succeeded: true, lastError: null, totalElapsedMs: 1182 } ``` ## Gotchas - **Only one wrapped node**: Retry wraps a single node. For a multi-step subgraph, use **Try/Catch** (no retry) or put the multi-step work in a separate workflow triggered by **Emit Event** and retry the emit. - **Idempotency**: retry assumes the wrapped call is safe to repeat. If it isn't (e.g. "create charge" would double-charge), add an idempotency key to the call, or use Try/Catch with no retries. - **Backoff cap**: even with `backoffMultiplier: 2` the engine enforces a 60s per-attempt cap so workflows don't stall indefinitely. - **HTTP response-aware retry not supported yet**: Retry fires on any thrown error. If the API returns a 200 with `{success: false}` in the body, Retry doesn't catch that — add a downstream If/Else to convert the success-with-error into a failure and wrap *that* path.