Loop Node 🔁
Repeats execution of a sub-path while a condition is true, or iterates over a collection.
Node type: loopNode
Category: Flow Control
Actor: loopNode (1 thread)
Description
The Loop Node enables repeated execution of a workflow path. It supports two loop types:
- Standard Loop — repeats while a JUEL condition is
true - Collection Loop — iterates over each item in an array variable
The loop body is the path of nodes connected after the Loop Node. Each iteration executes the body, then returns control to the Loop Node to check the condition again.
Properties
| Property | Type | Required | Description |
|---|---|---|---|
loopExpression | text | Conditional | JUEL condition for Standard Loop: ${count < 5} |
loopVariable | text | Conditional | Variable name for the current item in Collection Loop |
maxIterations | number | No | Safety cap on maximum iterations (default: 100) |
The loop type is inferred:
- If
loopExpressionis set → Standard Loop - If a collection variable is configured → Collection Loop
Standard Loop
Repeats while the expression evaluates to true. The loop body executes, then the condition is re-evaluated.
loopExpression: ${retryCount < 3}
maxIterations: 5
Pattern:
[Set Variable: retryCount = 0]
↓
[Loop Node: ${retryCount < 3}]
↓ (loop body)
[Service Task: Call API]
↓ successFlow
[Loop Node: exit] ← connect back to Loop Node exit
↓ (on exit)
[Continue]
Use a Set Variable node inside the loop body to increment the counter: retryCount → {retryCount + 1}. The loop exits when retryCount reaches 3.
Collection Loop (Multi-Instance)
Iterates over each item in an array. The loop body executes once per item. The current item is available as the loopVariable.
collection: orderItems
loopVariable: currentItem
[SQL: Fetch all pending orders → {orderList}]
↓
[Loop: collection=orderList, variable=currentOrder]
↓
[Service Task: Process order {currentOrder.id}]
↓
[AI Task: Generate confirmation for {currentOrder.customerEmail}]
↓
[Service Task: Send Email to {currentOrder.customerEmail}]
↓ (loop back automatically)
[After loop: Log completion]
Each iteration has access to:
{currentOrder}— the current item (object or primitive){currentOrder.id}— properties of the current item if it's an object{loopIndex}— zero-based index of the current iteration (built-in)
Outputs
After the loop completes:
| Variable | Type | Description |
|---|---|---|
{loopIndex} | number | Index of the last completed iteration |
{loopCount} | number | Total number of iterations completed |
| Any variables set in loop body | — | Last iteration's values are retained |
Connections
| Connection | Description |
|---|---|
sequenceFlow (incoming) | Arrives from previous node |
sequenceFlow (loop body) | Goes into the loop body |
sequenceFlow (exit) | Goes to the node after the loop |
| Loop-back connection | From end of loop body back to the Loop Node (engine-managed) |
The designer shows the Loop Node with two outgoing paths: one entering the loop body and one exiting when the loop completes.
Max Iterations Safety
Always set maxIterations to prevent infinite loops in production:
maxIterations: 100
If the loop reaches maxIterations before the exit condition is met, the node fails with a LOOP_LIMIT_EXCEEDED error.
Example: Retry with Backoff
[Set Variable: retryCount=0, lastError='']
↓
[Loop Node: ${retryCount < 3 && lastError != 'SUCCESS'}]
↓
[Service Task: Call Payment API]
↓ errorFlow
[Set Variable: retryCount={retryCount+1}, lastError='FAILED']
↓ (wait)
[Script Task: sleep 1 second * retryCount]
↓ (loop back)
[Continue after loop]
Example: Process Collection in Batches
[SQL: Fetch unprocessed items → {itemList}]
↓
[Loop: collection=itemList, variable=item, maxIterations=500]
↓
[Service Task: POST to external API with {item.id}]
↓ successFlow
[SQL: UPDATE items SET processed=true WHERE id={item.id}]
↓ (loop back)
[Log: {loopCount} items processed]
Best Practices
- Always set
maxIterations— it prevents runaway loops consuming all engine threads - For collection loops, ensure the collection variable is an array (validate with Script Task or If/Else)
- Use a Set Variable node at the start of the loop body if you need loop-specific variables
- For heavy per-item processing, consider whether the items can be processed in parallel using Split/Join instead
- Long-running loops should have a timeout set on the Loop Node itself