Skip to main content
Execute multiple steps concurrently and combine their results. This pattern is ideal for independent tasks that can run simultaneously.
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 #4932, an output_variable set inside a branch is preserved after the parallel block. Earlier releases silently discarded it.
The user starts one workflow; independent steps run concurrently and merge results.

Quick Start

1

Define parallel workers

2

Run parallel workflow

Output:

API Reference

parallel()

Parameters

Accessing Results

After parallel execution, results are available in ctx.variables:
Index alignment under partial_ok. A failed branch contributes None at the same index as its step, so parallel_outputs[i] is always the result of parallel_step.steps[i]. As of PraisonAI #4203, a downstream reader of parallel_outputs[i] can no longer silently receive a later branch’s result once an earlier branch fails.

Named outputs from branches

Every step inside parallel([...]) may set output_variable. As of PraisonAI #4932, that write is visible to any step after the parallel block, keyed by that exact name — not just as a positional entry in parallel_outputs.
The branch’s own output_variable is stored under that exact name; the ordered parallel_outputs list is still there for aggregators that want positional access. Each branch runs against its own deep copy of the variables, so branches never race on one shared dict. After the block, only the keys a branch actually wrote merge back into the shared scope — untouched variables keep the parent’s own object.

Two branches wrote the same variable

When two branches write the same output_variable, declaration order wins — the later branch overwrites the earlier, matching what running the same steps sequentially would produce. The result never depends on which thread finishes first.
A WARNING names the key and both branches. Silent last-writer-wins was the exact bug #4932 removes, so a collision is reported rather than hidden.
If two branches want to write the same concept, give them distinct output_variable names (branch_a_score, branch_b_score) and combine them in the aggregator.

Examples

With Agents

Mixed Steps

Nested Parallel

Performance

Parallel execution uses Python’s ThreadPoolExecutor:
  • Concurrent I/O: Ideal for API calls, file operations
  • Thread-safe: Each step gets its own copy of variables
  • Automatic joining: All results collected before next step

Use Cases

How It Works


Best Practices

Branches must not depend on each other’s output in the same parallel group.
Rate-limited tools may need sequential execution or throttling despite parallel support.
Read the aggregated error list after ParallelExecutionError before retrying.
Large parallel results inflate context — summarise before merging downstream.
Two branches writing the same output_variable collide — the later-declared branch silently overwrites the earlier one (with a warning). That class of silent loss is exactly what #4932 fixes; distinct names (branch_a_score, branch_b_score) avoid it entirely.

Failure Handling

Choose how a parallel block reacts when a branch fails using the on_failure parameter.

Failure Strategies

Variable merging on failure. Under partial_ok, the variables a failed branch had already written before it failed are preserved — its own output_variable is never set (the failure path returns before that write), so a downstream reader cannot pick up a value the failure invented. Under fail_fast / fail_all, nothing from any branch is merged: the block raises before the merge runs. See Named outputs from branches.

Examples

Error Handling with partial_ok

Stop propagation from nested steps

A branch step with on_error="stop" halts the whole workflow, not just the parallel block — even under on_failure="partial_ok". As of PraisonAI #4203, parallel(...) propagates a nested stop up to the enclosing workflow, matching route() / loop() / repeat() / if_().
The interaction between the parallel block’s on_failure mode and a branch step’s on_error is:

Exception Handling with fail_fast/fail_all

Workflow Patterns

Overview of routing, parallel, loop, and repeat

Workflow Routing

Decision-based branching

Workflow Loop

Iterate over lists and files

Workflow Repeat

Repeat until a condition is met