Skip to main content
The loop() helper runs a step for each item in a list, CSV, or text file — ideal for batch processing and data pipelines.
Steps inside this pattern inherit the same max_retries, guardrails, and output_file policies as top-level steps. See Nested workflows → Retry, guardrails, and output_file.
As of PraisonAI #4947, an output_variable set inside a loop() body is preserved after the loop block — keyed by that exact name. Earlier releases silently discarded it. This is the sibling fix to #4932, which fixed the same defect in parallel(...).
The user triggers a batch job; each list item runs through the loop step.

Quick Start

1

Loop Over a List

Output:
2

Loop Over a CSV

3

Loop Over a Text File

Choosing an Iteration Source

Pick the parameter that matches where your items live.

API Reference

loop()

Parameters

Context Variables

Inside the loop, these variables are available:

Result Variables

After loop completion:

Named outputs from a loop body

Every step inside loop([...]) may set output_variable. As of PraisonAI #4947, that write is visible to any step after the loop block, keyed by that exact name — not just as a positional entry in loop_outputs.
The body step’s own output_variable is stored under that exact name; the ordered loop_outputs list is still there for aggregators that want positional access. A StepResult.variables={"foo": ...} write from inside the loop body works the same way: the key merges back into the enclosing scope after the loop.

Sequential vs parallel: same final scope

The two loop modes agree on the resulting variables scope. parallel=True is meant to be a faster way to run the same loop, not different semantics — so:
  • Both end with the last item’s value for a variable the body rewrites each iteration.
  • Both propagate body writes (output_variable and StepResult.variables) back to the enclosing scope.
  • Both keep loop control variables (item / var_name, loop_index, item.<key>) scoped to the loop.
The one thing sequential offers that parallel cannot is accumulation across iterations: since the sequential loop runs against the shared scope, iteration N reads iteration N-1’s writes.
This is deliberately unavailable in the parallel loop — its iterations run concurrently, so there is no “previous iteration” to read from.

Iteration collisions are not warned about

Unlike parallel(...), when the loop body rewrites the same output_variable on every iteration, no warning is logged. N iterations of one loop body necessarily write the body’s output_variable N times — that is the normal shape of a loop, not an accident. The final value is the last item’s, exactly as an unrolled sequence would produce. Compare with parallel(...), where two sibling branches writing the same variable does log a WARNING because that pattern almost always signals an authoring mistake. See Named outputs from branches for the parallel counterpart.

Loop control variables stay loop-scoped

The variables the loop injects itself — item (or your custom var_name), loop_index, and flattened item.<key> accessors for dict items — are loop machinery, not results. They stay scoped to the loop in both modes:
  • The sequential path restores whatever those names held before the loop (so a pre-existing variables={"item": "ORIGINAL"} is unaffected).
  • The parallel path drops them from the merged delta.
  • Even if a body step deliberately sets output_variable="item", that write is stripped so the two modes agree.

Examples

Custom Variable Name

CSV with Headers

Given data.csv:

With Agents

Chained Loops

Loop with Aggregation

Use Cases

How It Works


Best Practices

Catch exceptions inside the loop body so one bad row does not stop the entire batch.
Enable verbose mode or custom logging when iterating thousands of records.
Split massive CSVs into batches to limit memory and simplify retries.
Close files and connections even when individual items fail.
As of #4947, a body step’s output_variable is visible after the loop under that exact name — set to the last item’s value. loop_outputs still holds the ordered list of every iteration’s last-step output for aggregators that need positional access. Reach for the named variable when you only need the final value; reach for loop_outputs when you need all of them.

Error Handling

Workflow Patterns

Overview of routing, parallel, loop, and repeat patterns

Workflow Routing

Decision-based branching in workflows

Workflow Parallel

Run independent steps concurrently

Workflow Repeat

Repeat steps until a condition is met