Inconsistency of concurrent nodes #1047

Closed
opened 2026-02-20 17:42:53 -05:00 by yindo · 2 comments
Owner

Originally created by @vanner-young on GitHub (Nov 7, 2025).

Originally assigned to: @eyurtsev on GitHub.

Checked other resources

  • This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/).
  • I added a clear and detailed title that summarizes the issue.
  • I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example).
  • I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue.

Example Code

def run_node():
    class AgentState(TypedDict):
        node_1: str
        node_2: str
        node_3: str
        node_4: str
        node_5: str
        node_6: str

    def node1(state: AgentState):
        return { 'node_1': 'node1' }

    def node2(state: AgentState):
        return { 'node_2': 'node2' }

    def node3(state: AgentState):
        return { 'node_3': 'node3' }

    def node4(state: AgentState):
        return { 'node_4': 'node4' }

    def node5(state: AgentState):
        print('---> node5 exec', state)
        return { 'node_5': 'node5' }

    def node6(state: AgentState):
        return { 'node_6': 'node6' }

    with_builder = StateGraph(AgentState)
    with_builder.add_node('node1', node1)
    with_builder.add_node('node2', node2)
    with_builder.add_node('node3', node3)
    with_builder.add_node('node4', node4)
    with_builder.add_node('node5', node5)
    with_builder.add_node('node6', node6)

    with_builder.add_edge(START, 'node1')
    with_builder.add_edge(START, 'node2')
    with_builder.add_edge('node1', 'node3')
    with_builder.add_edge('node2', 'node4')
    with_builder.add_edge('node4', 'node6')
    with_builder.add_edge('node3', 'node5')
    with_builder.add_edge('node6', 'node5')
    with_builder.add_edge('node5', END)

    graph = with_builder.compile()

    display(Image(graph.get_graph().draw_mermaid_png(output_file_path='./graph.png')))
    response = graph.invoke({})

Error Message and Stack Trace (if applicable)


Description

Please refer to my code example above.

Strangely, the code example above produces the following result:

"---> node5 exec", which is printed twice.

However, if I modify the execution order of the process nodes as follows:

with_builder.add_edge(START, 'node1')
with_builder.add_edge(START, 'node2')
with_builder.add_edge('node1', 'node3')
with_builder.add_edge('node2', 'node4')
with_builder.add_edge('node3', 'node5')
with_builder.add_edge('node4', 'node5')
with_builder.add_edge('node5', END)

Then, "---> node5 exec", is only printed once.

It's strange that the original example only has one more execution node than the modified example, even though both are actually two concurrent execution lines. I believe that in both the original and modified examples, "---> node5 exec" should be printed twice.

I don't think this is a bug, but it's easily misleading.

System Info

system:
windows11

packages:
langgraph: v1.0.2

Originally created by @vanner-young on GitHub (Nov 7, 2025). Originally assigned to: @eyurtsev on GitHub. ### Checked other resources - [x] This is a bug, not a usage question. For questions, please use the LangChain Forum (https://forum.langchain.com/). - [x] I added a clear and detailed title that summarizes the issue. - [x] I read what a minimal reproducible example is (https://stackoverflow.com/help/minimal-reproducible-example). - [x] I included a self-contained, minimal example that demonstrates the issue INCLUDING all the relevant imports. The code run AS IS to reproduce the issue. ### Example Code ```python def run_node(): class AgentState(TypedDict): node_1: str node_2: str node_3: str node_4: str node_5: str node_6: str def node1(state: AgentState): return { 'node_1': 'node1' } def node2(state: AgentState): return { 'node_2': 'node2' } def node3(state: AgentState): return { 'node_3': 'node3' } def node4(state: AgentState): return { 'node_4': 'node4' } def node5(state: AgentState): print('---> node5 exec', state) return { 'node_5': 'node5' } def node6(state: AgentState): return { 'node_6': 'node6' } with_builder = StateGraph(AgentState) with_builder.add_node('node1', node1) with_builder.add_node('node2', node2) with_builder.add_node('node3', node3) with_builder.add_node('node4', node4) with_builder.add_node('node5', node5) with_builder.add_node('node6', node6) with_builder.add_edge(START, 'node1') with_builder.add_edge(START, 'node2') with_builder.add_edge('node1', 'node3') with_builder.add_edge('node2', 'node4') with_builder.add_edge('node4', 'node6') with_builder.add_edge('node3', 'node5') with_builder.add_edge('node6', 'node5') with_builder.add_edge('node5', END) graph = with_builder.compile() display(Image(graph.get_graph().draw_mermaid_png(output_file_path='./graph.png'))) response = graph.invoke({}) ``` ### Error Message and Stack Trace (if applicable) ```shell ``` ### Description Please refer to my code example above. Strangely, the code example above produces the following result: "---> node5 exec", which is printed twice. However, if I modify the execution order of the process nodes as follows: ``` with_builder.add_edge(START, 'node1') with_builder.add_edge(START, 'node2') with_builder.add_edge('node1', 'node3') with_builder.add_edge('node2', 'node4') with_builder.add_edge('node3', 'node5') with_builder.add_edge('node4', 'node5') with_builder.add_edge('node5', END) ``` Then, "---> node5 exec", is only printed once. It's strange that the original example only has one more execution node than the modified example, even though both are actually two concurrent execution lines. I believe that in both the original and modified examples, "---> node5 exec" should be printed twice. I don't think this is a bug, but it's easily misleading. ### System Info system: windows11 packages: langgraph: v1.0.2
yindo added the bugpending labels 2026-02-20 17:42:53 -05:00
yindo closed this issue 2026-02-20 17:42:53 -05:00
Author
Owner

@le-codeur-rapide commented on GitHub (Nov 7, 2025):

Hello @vanner-young,
I think this is indeed intended behaviour because this is how these pregel graph super steps are supposed to work.

I do'nt know if you are aware of this but langgraph let the user choose what behaviour they want between:

  • Defer: wait for nodes 3 and 6 to be completed before executing node 5
  • Normal behaviour: node 5 is executed if either node 3 or 6 are completed
    This is with the defer=True parameter of .add_edge see langgraph documentation on defer
@le-codeur-rapide commented on GitHub (Nov 7, 2025): Hello @vanner-young, I think this is indeed intended behaviour because this is how these pregel graph super steps are supposed to work. I do'nt know if you are aware of this but langgraph let the user choose what behaviour they want between: - Defer: wait for nodes 3 and 6 to be completed before executing node 5 - Normal behaviour: node 5 is executed if either node 3 or 6 are completed This is with the `defer=True` parameter of `.add_edge` see [langgraph documentation on defer](https://docs.langchain.com/oss/python/langgraph/use-graph-api#defer-node-execution)
Author
Owner

@casparb commented on GitHub (Nov 7, 2025):

Hi @vanner-young, this is actually the intended behavior of LangGraph, and understanding it requires knowing how the graph execution model works. Good summary @le-codeur-rapide.

LangGraph executes your graph in super-steps. Each super-step can be thought of as a single iteration over graph nodes, in parallel when possible. A node executes in a super-step when at least one of its incoming edges has executed.

In your example:

  • Super-step 1: node1, node2 runs
  • Super-step 2: node3, node4 runs
  • Super-step 3: node5, node6 runs
  • Super-step 4: node5 runs again because node6 just finished

So node5 doesn't execute twice due to a bug, it executes once per super-step. Since node5 has two incoming edges (from node3 and node6), it activates twice.

If you want to enforce that a node waits for all incoming edges to complete before running, use the defer parameter:

with_builder.add_node('node5', node5, defer=True)

With defer=True, node5 won't run until both node3 and node6 have completed.

I would encourage you, and everyone else confused by this behavior, to view your graph in Studio. You can step through each super-step and see which nodes activate. It makes things much clearer.

Docs

@casparb commented on GitHub (Nov 7, 2025): Hi @vanner-young, this is actually the intended behavior of LangGraph, and understanding it requires knowing how the graph execution model works. Good summary @le-codeur-rapide. LangGraph executes your graph in super-steps. Each super-step can be thought of as a single iteration over graph nodes, in parallel when possible. A node executes in a super-step when at least one of its incoming edges has executed. In your example: - **Super-step 1:** node1, node2 runs - **Super-step 2:** node3, node4 runs - **Super-step 3:** node5, node6 runs - **Super-step 4:** node5 runs again because node6 just finished So node5 doesn't execute twice due to a bug, it executes once per super-step. Since node5 has two incoming edges (from node3 and node6), it activates twice. If you want to enforce that a node waits for all incoming edges to complete before running, use the `defer` parameter: ```python with_builder.add_node('node5', node5, defer=True) ``` With `defer=True`, node5 won't run until both node3 and node6 have completed. I would encourage you, and everyone else confused by this behavior, to view your graph in [Studio](https://docs.langchain.com/oss/python/langgraph/local-server). You can step through each super-step and see which nodes activate. It makes things much clearer. Docs - [Thinking in LangGraph](https://docs.langchain.com/oss/python/langgraph/thinking-in-langgraph) - [Defer node execution](https://docs.langchain.com/oss/python/langgraph/use-graph-api#defer-node-execution)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#1047