Node with multiple incoming edges not executed correctly when combined with conditional edges #426

Closed
opened 2026-02-20 17:40:03 -05:00 by yindo · 5 comments
Owner

Originally created by @snopoke on GitHub (Jan 30, 2025).

Checked other resources

  • This is a bug, not a usage question. For questions, please use GitHub Discussions.
  • 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

builder = StateGraph(State)
builder.add_node("a", ReturnNodeValue("I'm A"))
builder.add_edge(START, "a")
builder.add_node("b", ReturnNodeValue("I'm B"))
builder.add_node("b2a", ReturnNodeValue("I'm B2a"))
builder.add_node("b2b", ReturnNodeValue("I'm B2b"))
builder.add_node("c", ReturnNodeValue("I'm C"))
builder.add_edge("a", "b")
builder.add_edge("a", "c")
builder.add_edge(["b2a", "b2b"], "c")
builder.add_edge("c", END)


def router(state: State) -> Sequence[str]:
    return state["which"]


builder.add_conditional_edges(
    "b",
    router,
    ["b2a", "b2b"],
)

builder.set_entry_point("a")
builder.set_finish_point("c")
graph = builder.compile()
print(graph.invoke({"aggregate": [], "which": "b2a"}))

>> Adding I'm A to []
>> Adding I'm B to ["I'm A"]
>> Adding I'm C to ["I'm A"]
>> Adding I'm B2a to ["I'm A", "I'm B", "I'm C"]
>> {'aggregate': ["I'm A", "I'm B", "I'm C", "I'm B2a"], 'which': 'b2a'}

Error Message and Stack Trace (if applicable)


Description

In the example code above (diagram below), I would expect node C to execute with inputs from A, B2a and B2b but instead it executes only with input from A (note that B2a and B2b have conditional edges to C).

download

Furthermore, making node C dependent on all three incoming nodes results in C not being executed at all:

builder.add_edge(["a", "b2a", "b2b"], "c")

New output:

Adding I'm A to []
Adding I'm B to ["I'm A"]
Adding I'm B2a to ["I'm A", "I'm B"]
{'aggregate': ["I'm A", "I'm B", "I'm B2a"], 'which': 'b2a'}

System Info

System Information

OS: Linux
OS Version: #52-Ubuntu SMP PREEMPT_DYNAMIC Thu Dec 5 13:09:44 UTC 2024
Python Version: 3.11.10 (main, Oct 8 2024, 00:19:50) [Clang 18.1.8 ]

Package Information

langchain_core: 0.3.25
langchain: 0.3.9
langchain_community: 0.3.3
langsmith: 0.1.137
langchain_anthropic: 0.2.3
langchain_openai: 0.2.3
langchain_text_splitters: 0.3.0

Optional packages not installed

langserve

Other Dependencies

aiohttp: 3.10.11
anthropic: 0.37.1
async-timeout: Installed. No version info available.
dataclasses-json: 0.6.7
defusedxml: 0.7.1
httpx: 0.24.1
jsonpatch: 1.33
numpy: 1.26.2
openai: 1.52.1
orjson: 3.10.0
packaging: 23.2
pydantic: 2.9.2
pydantic-settings: 2.6.0
PyYAML: 6.0.1
requests: 2.32.3
requests-toolbelt: 1.0.0
SQLAlchemy: 2.0.23
tenacity: 9.0.0
tiktoken: 0.8.0
typing-extensions: 4.12.2

Originally created by @snopoke on GitHub (Jan 30, 2025). ### Checked other resources - [x] This is a bug, not a usage question. For questions, please use GitHub Discussions. - [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 builder = StateGraph(State) builder.add_node("a", ReturnNodeValue("I'm A")) builder.add_edge(START, "a") builder.add_node("b", ReturnNodeValue("I'm B")) builder.add_node("b2a", ReturnNodeValue("I'm B2a")) builder.add_node("b2b", ReturnNodeValue("I'm B2b")) builder.add_node("c", ReturnNodeValue("I'm C")) builder.add_edge("a", "b") builder.add_edge("a", "c") builder.add_edge(["b2a", "b2b"], "c") builder.add_edge("c", END) def router(state: State) -> Sequence[str]: return state["which"] builder.add_conditional_edges( "b", router, ["b2a", "b2b"], ) builder.set_entry_point("a") builder.set_finish_point("c") graph = builder.compile() print(graph.invoke({"aggregate": [], "which": "b2a"})) >> Adding I'm A to [] >> Adding I'm B to ["I'm A"] >> Adding I'm C to ["I'm A"] >> Adding I'm B2a to ["I'm A", "I'm B", "I'm C"] >> {'aggregate': ["I'm A", "I'm B", "I'm C", "I'm B2a"], 'which': 'b2a'} ``` ### Error Message and Stack Trace (if applicable) ```shell ``` ### Description In the example code above (diagram below), I would expect node `C` to execute with inputs from `A`, `B2a` and `B2b` but instead it executes only with input from `A` (note that `B2a` and `B2b` have conditional edges to `C`). ![download](https://github.com/user-attachments/assets/7eb35de0-2d8c-4c25-9bae-2f18044dbb5f) Furthermore, making node `C` dependent on all three incoming nodes results in `C` not being executed at all: ```python builder.add_edge(["a", "b2a", "b2b"], "c") ``` New output: ``` Adding I'm A to [] Adding I'm B to ["I'm A"] Adding I'm B2a to ["I'm A", "I'm B"] {'aggregate': ["I'm A", "I'm B", "I'm B2a"], 'which': 'b2a'} ``` ### System Info System Information ------------------ > OS: Linux > OS Version: #52-Ubuntu SMP PREEMPT_DYNAMIC Thu Dec 5 13:09:44 UTC 2024 > Python Version: 3.11.10 (main, Oct 8 2024, 00:19:50) [Clang 18.1.8 ] Package Information ------------------- > langchain_core: 0.3.25 > langchain: 0.3.9 > langchain_community: 0.3.3 > langsmith: 0.1.137 > langchain_anthropic: 0.2.3 > langchain_openai: 0.2.3 > langchain_text_splitters: 0.3.0 Optional packages not installed ------------------------------- > langserve Other Dependencies ------------------ > aiohttp: 3.10.11 > anthropic: 0.37.1 > async-timeout: Installed. No version info available. > dataclasses-json: 0.6.7 > defusedxml: 0.7.1 > httpx: 0.24.1 > jsonpatch: 1.33 > numpy: 1.26.2 > openai: 1.52.1 > orjson: 3.10.0 > packaging: 23.2 > pydantic: 2.9.2 > pydantic-settings: 2.6.0 > PyYAML: 6.0.1 > requests: 2.32.3 > requests-toolbelt: 1.0.0 > SQLAlchemy: 2.0.23 > tenacity: 9.0.0 > tiktoken: 0.8.0 > typing-extensions: 4.12.2
yindo closed this issue 2026-02-20 17:40:03 -05:00
Author
Owner

@hinthornw commented on GitHub (Jan 30, 2025):

Your edges are saying "go to c after A complees or after both b2a and b2b":

builder.add_edge(["b2a", "b2b"], "c")
builder.add_edge("a", "c")

If you want to only go to a once all of a, b2a, and b2b are triggered, you'd do:

builder.add_edge(["b2a", "b2b", "a"], "c")

If you want to go to C only when both (a, b2a) complete OR when both (a, b2b) complete then you'd do:

builder.add_edge(["b2a", "a"], "c")
builder.add_edge(["b2b", "a"], "c")
@hinthornw commented on GitHub (Jan 30, 2025): Your edges are saying "go to c after A complees or after both b2a and b2b": ```python builder.add_edge(["b2a", "b2b"], "c") builder.add_edge("a", "c") ``` If you want to only go to a once all of a, b2a, and b2b are triggered, you'd do: ```python builder.add_edge(["b2a", "b2b", "a"], "c") ``` If you want to go to C only when both (a, b2a) complete OR when both (a, b2b) complete then you'd do: ```python builder.add_edge(["b2a", "a"], "c") builder.add_edge(["b2b", "a"], "c") ```
Author
Owner

@vbarda commented on GitHub (Feb 5, 2025):

@snopoke does the above answer help?

@vbarda commented on GitHub (Feb 5, 2025): @snopoke does the above answer help?
Author
Owner

@snopoke commented on GitHub (Feb 6, 2025):

Yes, thanks. I think the name of add_edge is a bit confusing since it's doing a lot more than that. Also a further example in the docs would be very helpful.

For graphs that are created dynamically this is going to make it hard to determine the correct configuration to ensure that all the right combinations are configured when there is conditional branching, especially when there are nodes in between the branch and the merge nodes.

@snopoke commented on GitHub (Feb 6, 2025): Yes, thanks. I think the name of `add_edge` is a bit confusing since it's doing a lot more than that. Also a further example in the docs would be very helpful. For graphs that are created dynamically this is going to make it hard to determine the correct configuration to ensure that all the right combinations are configured when there is conditional branching, especially when there are nodes in between the branch and the merge nodes.
Author
Owner

@vbarda commented on GitHub (Feb 6, 2025):

That's a fair point. We also recently updated the docs page here to cover this https://langchain-ai.github.io/langgraph/how-tos/branching/#parallel-node-fan-out-and-fan-in-with-extra-steps

@vbarda commented on GitHub (Feb 6, 2025): That's a fair point. We also recently updated the docs page here to cover this https://langchain-ai.github.io/langgraph/how-tos/branching/#parallel-node-fan-out-and-fan-in-with-extra-steps
Author
Owner

@kamelCased commented on GitHub (Apr 30, 2025):

@hinthornw Can we apply the same logic to conditional edges? I would like to do something like builder.add_conditional_edges(["b2a", "b2b"], determine_condition)

@kamelCased commented on GitHub (Apr 30, 2025): @hinthornw Can we apply the same logic to conditional edges? I would like to do something like builder.add_conditional_edges(["b2a", "b2b"], determine_condition)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#426