Skip to content

Workflow limits and snapshots

Workflows orchestrate bounded in-process execution. They do not persist instruction pointers or resume running steps after a restart. A database-backed queue is not a durable workflow checkpoint engine.

ControlDefault/range and boundary
YAML entry nestingMaximum structural nesting depth 16; distinct from sub-workflow call depth.
Nested workflow callsMaximum call depth counter 4; static cycles rejected while baking, runtime also checks depth. Root starts at depth 0; a call from depth 4 is refused.
while/until max_iterationsRequired 1–1000; exhaustion is an error, not a truncated successful result.
for_each max_concurrencyDefault 4, integer 1–16, inside for_each mapping.
parallel max_concurrencyAbsent allows all declared children, explicit integer 1–16, sibling of parallel. It bounds child entries, not all nested leaves globally.
root max_concurrentOptional positive u32, eligible detached target runs; separate project/key durable admission budget.
script retry times1–10 additional retries, up to 11 attempts; all share deadline.
task chainsDeployment max depth normally 100, not the workflow nesting depth. Python/TypeScript task API only.

There is no published universal “for_each handles unlimited items safely” guarantee. The engine allocates iteration futures/result arrays for the resolved input; max_concurrency limits active work, not input/result memory. Bound input arrays and output size at your application boundary. Nested fan-outs multiply, and deployment worker/connection/resource limits can admit less than the YAML asks.

Root timeout selects a total run budget, fallback project/deployment default (normally 30 seconds), clamped by the connected/detached ceilings (normally 300/1800 seconds). A step script’s own timeout and a child workflow’s declared timeout do not reset the parent deadline. Child calls inherit the remaining budget; rounding to seconds is an implementation granularity, not a fresh full window. Retry delays, semaphore waits, resource requests and every iteration consume the same budget.

Lane selection uses the root declared budget, not the shrinking remainder. A long workflow keeps long-run lane semantics even near completion. A queued workflow holds one admitted long-run permit for its run; its one-shot steps and parallel branches do not recursively acquire a new permit per child. Outside queued admission, deployment lane limits still apply and overload can fail a step.

Root max_concurrent/concurrency_key apply to eligible background endpoint runs/tasks, not synchronous requests or ordinary test/schedule acceptance. Key absent uses workflow:<folder/name>; a declared key shares a project-local budget with scripts/workflows. Same key does not make inconsistent declared limits converge into a new global value: each accepted run keeps its declaration. Invalid/zero limits may be ignored and publish warns; they are not a disable switch.

Waiting rows preserve frozen target/input/principal and survive process restart. They have no caller Authorization credential; caller_token resources cannot use it later. Queue depth and lane caps still apply. A run already executing when its instance dies is marked interrupted; it is not resumed/retried from the last completed step. At-least-once external effects, idempotency and compensation remain application responsibilities.

The engine checks cancellation before entries, retry attempts and loop iterations, ahead of deadline checks. It cannot be swallowed by on_error: continue or catch. A worker-owning lane can terminate its current process; shared workers, SQL and unsupported paths can finish the current step first. Rust execution is not cancellable in 1.1.0. The guarantee is boundary-aware stopping, not universal instantaneous interruption or rollback.

Parallel/for_each use fail-fast joining: one unhandled failure drops sibling execution futures. Requests/writes already sent may complete and some sibling end logs may be absent. Do not infer “did not happen” from a missing end row. A script catch/continue can convert a child failure to success; there is no wrapper-wide retry/catch policy. Partial fan-out effects must be reconciled explicitly.

Endpoint publish bakes the workflow definition, each step script main source and recursively nested workflow definitions. Changes in editor/live rows take effect for direct test/schedule targets, but public published callers keep the snapshot until republish. Rollback restores that baked definition/source, not external databases or live resources.

Important 1.1.0 exception: workflow-step uses: modules are resolved live before each workflow run, including all nested steps. Unlike an ordinary script endpoint’s frozen non-live graph, these dependency modules are not persisted in workflow snapshots. Shared-module edits can change a published workflow without republishing; rollback does not restore those modules. Explicit (live), direct drafts/schedules and legacy script snapshots without frozen module metadata also resolve live. Script dependencies describes the distinction.

Resources are resolved using step/dependency folder context and current resource data; the YAML is not a universal secret snapshot. Prefer broker helpers and do not serialize authentication projections into outputs. Steps inherit principal/request; nested workflows do not add privileges. A raw-body workflow endpoint has only path/query arguments under $inputs; a script step reads the raw bytes from request context.

Use overview’s no-resource case, add bounded concurrency and deliberate failures in an isolated project, inspect result order plus per-attempt logs, and compare draft versus published execution after an edit. Logs identify nested paths such as sub.inner or sub[0].inner; step_index alone is not globally unique—iteration/attempt/path matter. A rerun is a new execution with possible repeated effects, not checkpoint restoration.

No native approval, wait-event, durable sleep/checkpoint or external-job adapters exist. Long waits should not be disguised as unbounded loops; use a deliberate task/schedule design with state in your application if needed. This page provides manual cases only; no live cancellation/restart/side-effect verification was performed here.