The execution model
Predicting what a workflow will do requires the real execution model — not a plausible one. This page states it precisely.
Execution is sequential
Section titled “Execution is sequential”The engine runs one flow frame as a single queue: it takes the next ready node, runs it to completion, then takes the next. Two branches with no dependency between them are interleaved, never concurrent. Loop iterations run one after another.
Consequences:
- Node order within independent branches is not guaranteed — never build logic that depends on “branch A finishes before branch B”.
- Side effects happen one at a time, which makes execution traces exact and replayable.
- A slow node delays everything after it in the queue. For long waits, that is fine: Wait is a durable timer (up to 30 days) that survives restarts and doesn’t burn compute.
When a node runs, skips, or blocks
Section titled “When a node runs, skips, or blocks”A node becomes ready when every incoming edge’s source has finished. Then:
- If all inputs arrived normally, it runs.
- If any incoming edge comes from a branch that was not taken, the node is blocked — it is skipped, and the block propagates to everything downstream of it.
- Join Paths is the one exemption. It runs when any incoming branch arrives, which makes it the required primitive for converging conditional branches — not an optional tidiness node.
Each input handle accepts exactly one incoming edge; a second connection to the same handle is refused.
Skip reasons
Section titled “Skip reasons”A skipped node records why — read it instead of guessing:
| Reason | Meaning |
|---|---|
only_when_false | The node’s own run-condition evaluated false. |
upstream_skipped | Something it depends on was skipped. |
all_branches_blocked | Every path leading to it was an untaken branch. |
no_active_path | No active route reached it this run. |
Statuses
Section titled “Statuses”Node and flow executions: pending, running, completed, completed with error, failed, skipped, cancelled. Workflow executions can additionally be paused — a running execution can be paused, resumed, or cancelled from run details or the API.
What each node execution records
Section titled “What each node execution records”Every node execution persists three inspectable layers — the foundation of all debugging:
- Inputs — the actual values that arrived.
- Resolved configuration — every prop after expression evaluation. A null or empty resolved value on a green node is a failure in disguise (
0andfalseare valid; empty is not). - Outputs — the values downstream nodes will see, on named output ports.
cai exec tree <workflowExecutionId>cai exec ports <nodeExecutionId> --jsonHow things fail
Section titled “How things fail”Node errors have four types: input errors (a wired value didn’t satisfy the input — a data-type mismatch fails loudly here), process errors (the action itself failed: missing parameter, bad value, API error), output errors, and unknown errors.
The failure mode that does not announce itself: expressions null-propagate. A missing field doesn’t throw — it flows through as empty, and comparisons against empty values fail closed (match nothing). The symptom is a green run with wrong or empty results; the diagnosis is reading resolved configs, not error messages. A completed run with empty output should be treated as a failure until the values are read and explained.
Continue on error
Section titled “Continue on error”Off (default): a node failure aborts the flow frame immediately. On: the failed node’s output becomes null, the main path continues, and the run ends completed with error. Enabling it also offers an optional Error output port carrying the error details — wire it to build a recovery branch. Toggling continue-on-error off deletes that port and every edge from it.
Returning results
Section titled “Returning results”A flow’s output comes from Return response nodes. When several complete in one run, their result objects merge in execution order — a later node silently overwrites an earlier value for the same field. One Return response per branch end is the predictable pattern.
Hard limits
Section titled “Hard limits”| Limit | Value |
|---|---|
| Node executions per run | 25,000 — the run fails when exceeded. |
| Subflow nesting depth | Workflow setting, default 100 (configurable 1–10,000). |
| Wait node duration | Up to 30 days, durable. |
The per-run budget is the one loops hit: a flow over 5,000 items with 6 nodes inside the iteration exhausts it.
Example prompts
Section titled “Example prompts”This run is green but the output is empty. Walk the execution tree, find the first node whose resolved config or output is empty, and explain why.These two branches need to converge on one Slack message. Wire it so the message sends when either branch runs — use Join Paths, don't wire both edges into the Slack node.