---
title: Parallel Workflows in LangGraph
description: "Parallel workflows allow multiple independent nodes to execute concurrently within a LangGraph application. They are useful when several tasks can process the same input without depending on one another. A typical parallel workflow follows this pattern graph LR START(\\[🚀 START\\])…"
url: "https://csestudy247.com/docs/parallel-workflows-in-langgraph/"
updated: "2026-08-16"
category: LangGraph
---

# Parallel Workflows in LangGraph

Parallel workflows allow multiple independent nodes to execute concurrently within a LangGraph application. They are useful when several tasks can process the same input without depending on one another.

A typical parallel workflow follows this pattern

 graph LR START(\[🚀 START\]) --> A\[1. 📄 Summary\] START --> B\[2. 💬 Sentiment\] START --> C\[3. 🔑 Keywords\] A --> D\[4. 📊 Final Report\] B --> D C --> D D --> END(\[✅ END\])

This is known as **fan-out → parallel execution → fan-in**.

---

## 1. When to Use Parallel Workflows

Use parallel execution when tasks are independent.

Common examples:

- Generate a summary and keywords from the same document.
- Run multiple evaluation criteria simultaneously.
- Ask multiple agents for independent perspectives.
- Perform independent data-processing operations.

If one task requires the output of another, a sequential workflow is usually more appropriate.

---

## 2. State Updates

LangGraph nodes communicate through a shared state

```python

                from typing import TypedDict

class State(TypedDict):
    text: str
    summary: str
    sentiment: str
    keyword: str
    final_answer: str

# Parallel nodes should normally return partial state updates—only the fields they produce.

def summarize(state: State):
    result = llm.invoke(
        f"Summarize this text into 2 sentences: {state['text']}"
    )

    return {
        "summary": result.content
    }
```

Here, the node updates only `summary`; it does not return the entire state.

---

## 3. Example: Parallel LLM Workflow

The following example uses **Ollama with `gemma3:1b`**, but you can use **any compatible LLM**, such as OpenAI, Anthropic, Google Gemini, or other LangChain-supported models, to perform the three independent analysis tasks.

### Create the Model

```python

                from langchain_ollama import ChatOllama

llm = ChatOllama(model="gemma3:1b)
```

### Define the Nodes

```javascript

                def summarize(state: State):
    result = llm.invoke(
        f"Summarize this text into 2 sentences: {state['text']}"
    )
    return {"summary": result.content}

def sentiment(state: State):
    result = llm.invoke(
        f"Analyze the sentiment of this text. "
        f"Return Positive, Negative, or Neutral with a short reason:\n"
        f"{state['text']}"
    )
    return {"sentiment": result.content}

def extract_keyword(state: State):
    result = llm.invoke(
        f"Extract the 5 most important keywords "
        f"from this text:\n{state['text']}"
    )
    return {"keyword": result.content}
```

These three nodes are independent because they all require only `state["text"]`.

---

## 4. Fan-Out and Fan-In

Create the graph and add the nodes:

```python

                from langgraph.graph import StateGraph, START, END

builder = StateGraph(State)

builder.add_node("summarize", summarize)
builder.add_node("sentiment", sentiment)
builder.add_node("keyword", extract_keyword)
builder.add_node("final_report", final_report)
```

### Fan-Out

Connect `START` to the independent nodes:

```python

                builder.add_edge(START, "summarize")
builder.add_edge(START, "sentiment")
builder.add_edge(START, "keyword")
```

This creates the parallel branches.

### Fan-In

Connect the branches to the final node:

```python

                builder.add_edge("summarize", "final_report")
builder.add_edge("sentiment", "final_report")
builder.add_edge("keyword", "final_report")

builder.add_edge("final_report", END)
```

The final node runs after the required upstream branches have completed.

---

## 5. Aggregating the Results

The final node can use all results stored in the state:

```python

                def final_report(state: State):

    prompt = f"""
Create a final report.

Original Text:
{state['text']}

Summary:
{state['summary']}

Sentiment:
{state['sentiment']}

Keywords:
{state['keyword']}
"""

    result = llm.invoke(prompt)

    return {
        "final_answer": result.content
    }

Compile and execute the graph:

graph = builder.compile()

result = graph.invoke({
    "text": "Artificial Intelligence is changing software development."
})

print(result["final_answer"])
```

---

## 6. Reducers

A reducer is required when multiple parallel nodes write to the **same state key** and their results need to be combined.

For example:

```
from typing import Annotated, TypedDict
import operator

class State(TypedDict):
    scores: Annotated[list[int], operator.add]
```

If parallel nodes return:

```
Node A → [8]
Node B → [7]
Node C → [9]
```

the reducer combines them into:

```
[8, 7, 9]
```

**Without a reducer, multiple writes to the same state key can conflict.**

Reducers are **not needed** when each node updates a different key:

```
Summary   → summary
Sentiment → sentiment
Keywords  → keyword
```

---

Complete Code

```python

                from langchain_ollama import ChatOllama
from langgraph.graph import StateGraph, START, END
from typing import TypedDict

# 1. Create LLM
llm = ChatOllama(
    model="gemma3:1b"
)

# 2. Define State
class State(TypedDict):
    text: str
    summary: str
    sentiment: str
    keyword: str
    final_answer: str

# 3. Parallel Node 1
def summarize(state: State):

    result = llm.invoke(
        f"Summarize this text into 2 sentences: {state['text']}"
    )

    return {
        "summary": result.content
    }

# 4. Parallel Node 2
def sentiment(state: State):

    result = llm.invoke(
        f"Analyze the sentiment of this text. "
        f"Return Positive, Negative, or Neutral with a short reason:\n"
        f"{state['text']}"
    )

    return {
        "sentiment": result.content
    }

# 5. Parallel Node 3
def extract_keyword(state: State):

    result = llm.invoke(
        f"Extract the 5 most important keywords "
        f"from this text:\n{state['text']}"
    )

    return {
        "keyword": result.content
    }

# 6. Final Node
def final_report(state: State):

    prompt = f"""
Create a final report of this text.

Original Text:
{state['text']}

Summary:
{state['summary']}

Sentiment:
{state['sentiment']}

Keywords:
{state['keyword']}

Give a clear final response.
"""

    result = llm.invoke(prompt)

    return {
        "final_answer": result.content
    }

# 7. Build Graph
builder = StateGraph(State)

builder.add_node("summarize", summarize)
builder.add_node("sentiment", sentiment)
builder.add_node("keyword", extract_keyword)
builder.add_node("final_report", final_report)

# START → Parallel Nodes
builder.add_edge(START, "summarize")
builder.add_edge(START, "sentiment")
builder.add_edge(START, "keyword")

# Parallel Nodes → Final Node
builder.add_edge("summarize", "final_report")
builder.add_edge("sentiment", "final_report")
builder.add_edge("keyword", "final_report")

# Final Node → END
builder.add_edge("final_report", END)

# 8. Compile Graph
graph = builder.compile()

# 9. Run Graph
result = graph.invoke({
    "text": "Artificial Intelligence is changing software development."
})

# 10. Print Final Answer
print(result["final_answer"])
```

---

## Key Concepts

| Concept | Description |
| --- | --- |
| **Parallel Workflow** | Executes independent nodes concurrently |
| **Fan-Out** | Splits execution into multiple branches |
| **Fan-In** | Converges branches into a later node |
| **Partial Update** | Node returns only the fields it changes |
| **Reducer** | Defines how multiple updates to the same field are combined |

The core principle is:

> Use parallel execution for independent work, partial state updates for clean state management, and reducers when multiple branches must contribute to the same state field.

Q.1 In a parallel LangGraph workflow, why does returning the entire state from multiple nodes often cause an InvalidUpdateError?

- The graph encounters a conflict because multiple nodes attempt to overwrite the same state keys simultaneously.
- Parallel nodes are not allowed to access the same state dictionary at the same time.
- Returning the full state exceeds the memory limit allocated for parallel branches.
- The StateGraph class only supports partial updates for its internal nodes.

    Think about what happens when two nodes try to update the same state field simultaneously.    [Discuss it](#)

Explanation

When multiple parallel nodes return the entire state, they may overwrite the same keys at the same time. LangGraph detects this conflicting update and raises an InvalidUpdateError to prevent inconsistent state.

Q.2 Which strategy is recommended to prevent conflicts when updating state in parallel workflows?

- Wrap every node in a try-except block.
- Use global variables instead of LangGraph state.
- Return a partial dictionary containing only the keys updated by that node.
- Insert a Wait node between every parallel branch.

    Think about returning only the data your node actually changes.    [Discuss it](#)

Explanation

Each parallel node should return only the specific state fields it modifies. Returning partial updates minimizes conflicts and allows LangGraph to merge updates correctly.

Q.3 When defining a state key that aggregates results from multiple parallel nodes into a list, which Python component is commonly used as the reducer?

- operator.add
- functools.reduce
- math.sum
- collections.Counter

    It comes from Python's operator module and concatenates lists.    [Discuss it](#)

Explanation

LangGraph commonly uses operator.add as a reducer to merge list outputs from multiple parallel branches by appending each branch's results into a single shared list.

Q.4 What is the main purpose of a Parallel Workflow in LangGraph?

- To execute all tasks strictly one after another.
- To execute independent tasks concurrently.
- To remove the need for a state.
- To replace the LLM with a database.

    Think about tasks that do not depend on each other.    [Discuss it](#)

Explanation

A Parallel Workflow allows independent nodes to execute concurrently, which can reduce execution time compared with running the same tasks sequentially.

Q.5 Why is structured output especially important in automated LangGraph workflows?

- It reduces internet bandwidth usage.
- It guarantees that the LLM returns predictable fields that downstream nodes can process reliably.
- It automatically trains the language model during execution.
- It eliminates the need for prompt engineering.

    Think about what happens if later nodes expect fields that may not exist.    [Discuss it](#)

Explanation

Structured output ensures every node receives data in a consistent format, reducing parsing errors and making automated workflows more reliable and production-ready.

Q.6 Which specific LangGraph function generates the final executable graph after all nodes and edges are defined?

- graph.build()
- graph.run()
- graph.compile()
- graph.initialize()

    Think about the final preparation step before calling invoke().    [Discuss it](#)

Explanation

The compile() method validates the graph structure and produces an executable graph object that supports methods such as invoke(), stream(), and batch().

Q.7 What do Fan-Out and Fan-In represent in a LangGraph parallel workflow?

- Fan-Out combines results and Fan-In splits the workflow.
- Fan-Out and Fan-In both execute a single node.
- Fan-Out splits the workflow into parallel branches, while Fan-In brings the branches together.
- Fan-Out and Fan-In are used only for error handling.

    Think about splitting work first and combining it later.    [Discuss it](#)

Explanation

Fan-Out splits execution into multiple independent branches, while Fan-In brings those branches together at a later node, such as a final aggregation or reporting node.

Q.8 Why are reducers particularly useful when implementing parallel execution?

- They automatically create new graph nodes.
- They resolve concurrent updates by defining how multiple values should be combined.
- They eliminate the need for shared state.
- They improve GPU performance.

    Think about avoiding conflicts when several branches finish together.    [Discuss it](#)

Explanation

Without reducers, simultaneous writes to the same key would conflict. Reducers provide deterministic merge logic so parallel outputs can be safely combined.

Q.9 In a parallel essay evaluation system, why might different evaluator nodes run simultaneously?

- To evaluate different aspects of the essay, such as grammar, content, and structure, at the same time.
- To duplicate identical work for backup.
- Because LangGraph requires every node to execute in parallel.
- To reduce the size of the state dictionary.

    Think about dividing a large evaluation into independent tasks.    [Discuss it](#)

Explanation

Parallel execution allows specialised evaluators to assess different dimensions of the essay independently, reducing overall execution time before their results are merged.

Q.10 What happens if you use a standard LLM without with_structured_output() in an automated workflow?

- The LLM refuses to provide scores.
- The model cannot process long inputs.
- LangGraph raises a ModelNotFoundError.
- The LLM may return inconsistent text formats that cause parsing errors in downstream code.

    Compare a free-form paragraph with a predefined JSON structure.    [Discuss it](#)

Explanation

Without structured output, the model may produce responses in different formats each time, making it difficult for subsequent nodes to reliably extract fields such as score, feedback, or recommendations.
