For Each

<for-each> executes a durable body once for every item in a runtime array.

Example

WOML
<for-each id="processOrders" items="{{context.steps.load.orders}}" concurrency="4">
  <step id="processOrder" retry="3">
    <script>return { id: context.item.id, index: context.iteration.index };</script>
  </step>
  <result value="{{context.steps.processOrder}}" />
</for-each>

items is one exact reference resolving to an array. concurrency accepts 1 through 64 and defaults to 1. At most 10,000 items are admitted.

Iteration context

Each body receives context.item, the zero-based context.iteration.index, and context.iteration.total. Body outputs are isolated to that item.

Ordered aggregate

The optional final result is collected in input order, even when iterations finish out of order. The loop publishes { total, succeeded, results }; without <result>, it omits results.

One failed item stops new admission and fails the loop after owned work settles. Nested loops, forks, and approvals are not supported inside the first loop profile.

Build a complete item-processing workflow

WOML
<step id="loadOrders" name="Load orders">
  <script>
    return {
      orders: [
        { id: "a", amount: 20 },
        { id: "b", amount: 35 },
        { id: "c", amount: 50 }
      ]
    };
  </script>
</step>

<for-each
  id="processOrders"
  name="Process orders"
  items="{{context.steps.loadOrders.orders}}"
  concurrency="2"
>
  <step id="calculate" retry="3">
    <script>
      return {
        orderId: context.item.id,
        total: context.item.amount * 1.2,
        index: context.iteration.index
      };
    </script>
  </step>
  <result value="{{context.steps.calculate}}" />
</for-each>

<step id="summary">
  <script>
    return {
      processed: context.steps.processOrders.succeeded,
      results: context.steps.processOrders.results
    };
  </script>
</step>

WOML captures the array when the loop opens. Each index gets durable identity (run, for-each, index). Two iterations may execute at once, but the final results array remains in original input order.

Empty and failed inputs

An empty array succeeds immediately with zero totals. A non-array value is a runtime reference/type failure rather than an empty loop.

When one item fails, WOML stops admitting new iterations, settles work it already owns, and fails the loop. Completed item effects are not replayed during recovery. Ambiguous interrupted effects fail closed.

Choose structural or JavaScript iteration

Use <for-each> when items need separate retries, recovery, cancellation, progress, or bounded concurrency. Use array.map() or a JavaScript loop for small pure transformations that should succeed or fail together.

The first for-each profile does not support nested for-each, fork, or approval inside its body. It supports steps, reusable steps, parallel groups, choices, and switches.

Inspect progress

Foreground output, background logs, woml inspect, and woml get expose safe iteration counts and failed identity without storing item values in operational summaries. The maximum admitted input is 10,000 items and concurrency is bounded from 1 through 64.