Adding conditional edge from the entry point node does not seem to pass the Overall state to the validation function #465

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

Originally created by @sand-heap on GitHub (Feb 20, 2025).

Discussed in https://github.com/langchain-ai/langgraph/discussions/3387

Originally posted by sand-heap February 11, 2025
Pretty much what the title says. I can explain further with code

...

@dataclass
class InputState:
  some_field: int

class OverAllState(messages):
  some_field: int

def validate_transition(state: OverallState)->List:
  if state.get("some_field") == 1:
    return END
  else:
    return "other_node"

def some_node(input: InputState)->OverAllState:
  return {"some_field": input.some_field+1}

workflow = StateGraph(OverAllState, input=InputState)
workflow.set_entry_point("node")
workflow.add("node", some_node)
workflow.add("other_node", other_node)
workflow.add_conditional_edges("node", validate_transition,...)
...
# add end
...

I would expect the validate_transition to receive the OverallState but for some reason it gets the InputState ?!

However if i add a mock_node between node and other_node and make the edge from mock_node conditional instead and have a sequential flow from node -> mock_node, the behavour is as expected.

can you please help? ++ @vbarda @hinthornw

Originally created by @sand-heap on GitHub (Feb 20, 2025). ### Discussed in https://github.com/langchain-ai/langgraph/discussions/3387 <div type='discussions-op-text'> <sup>Originally posted by **sand-heap** February 11, 2025</sup> Pretty much what the title says. I can explain further with code ``` ... @dataclass class InputState: some_field: int class OverAllState(messages): some_field: int def validate_transition(state: OverallState)->List: if state.get("some_field") == 1: return END else: return "other_node" def some_node(input: InputState)->OverAllState: return {"some_field": input.some_field+1} workflow = StateGraph(OverAllState, input=InputState) workflow.set_entry_point("node") workflow.add("node", some_node) workflow.add("other_node", other_node) workflow.add_conditional_edges("node", validate_transition,...) ... # add end ... ``` I would expect the `validate_transition` to receive the `OverallState` but for some reason it gets the `InputState` ?! However if i add a `mock_node` between `node` and `other_node` and make the edge from `mock_node` conditional instead and have a sequential flow from `node` -> `mock_node`, the behavour is as expected. can you please help? ++ @vbarda @hinthornw </div>
yindo added the bug label 2026-02-20 17:40:16 -05:00
yindo closed this issue 2026-02-20 17:40:16 -05:00
Author
Owner

@YassinNouh21 commented on GitHub (Feb 25, 2025):

Hey @sand-heap
Let's clarify more the problem u are facing

The problem occurs at the transition point when you're trying to change from an input state to an overall state in your LangGraph workflow.

Specifically, the issue is that:

Your some_node function takes an InputState as input and returns what's intended to be part of the OverallState
When you set up a conditional edge directly from this node using validate_transition, the condition function receives the raw output from some_node (which is derived from InputState)
However, your validate_transition function is expecting to receive the full OverallState that should have been updated with the output from some_node


You can solve it using the new Command routing feature

LangGraph provides a Command object that gives you more control over graph execution flow. Let's implement a solution using this feature.

Here's how we can implement your feature correctly:

from typing import List, TypedDict, Annotated, Literal, TypeVar, Optional
from dataclasses import dataclass
import operator

from langgraph.graph import StateGraph, START, END
from langgraph.types import Command

# Define input state as a dataclass
@dataclass
class InputState:
    some_field: int

# Define overall state as a TypedDict
class OverallState(TypedDict):
    some_field: int
    # You could add more fields here if needed

# Node that receives InputState and returns a Command
def some_node(input: InputState) -> Command[Literal["other_node"]]:
    print(f"Called some_node with input field value: {input.some_field}")
    # Return a Command that updates the state and specifies where to go next
    return Command(
        update={"some_field": input.some_field + 1},
        goto="other_node" if input.some_field < 5 else END
    )

# Another node that uses the updated state
def other_node(state: OverallState) -> OverallState:
    print(f"Called other_node with state field value: {state['some_field']}")
    # You can do additional processing here
    return {"some_field": state["some_field"] + 2}

# Create the workflow
workflow = StateGraph(OverallState, input=InputState)
workflow.set_entry_point("node")
workflow.add_node("node", some_node)
workflow.add_node("other_node", other_node)

# Instead of using conditional edges, we're using Command to control flow
workflow.add_edge("other_node", "node")

# Compile the graph
compiled_workflow = workflow.compile()

Try to complie it using this and it will has no problem

# Test the workflow
result = compiled_workflow.invoke(InputState(some_field=1))
print("Final result:", result)

# Test with different input
result2 = compiled_workflow.invoke(InputState(some_field=5))
print("Second result:", result2)
@YassinNouh21 commented on GitHub (Feb 25, 2025): Hey @sand-heap Let's clarify more the problem u are facing The problem occurs at the transition point when you're trying to change from an input state to an overall state in your LangGraph workflow. Specifically, the issue is that: Your some_node function takes an InputState as input and returns what's intended to be part of the OverallState When you set up a conditional edge directly from this node using validate_transition, the condition function receives the raw output from some_node (which is derived from InputState) However, your validate_transition function is expecting to receive the full OverallState that should have been updated with the output from some_node --- You can solve it using the new Command routing feature LangGraph provides a `Command` object that gives you more control over graph execution flow. Let's implement a solution using this feature. Here's how we can implement your feature correctly: ```python name=improved_workflow.py from typing import List, TypedDict, Annotated, Literal, TypeVar, Optional from dataclasses import dataclass import operator from langgraph.graph import StateGraph, START, END from langgraph.types import Command # Define input state as a dataclass @dataclass class InputState: some_field: int # Define overall state as a TypedDict class OverallState(TypedDict): some_field: int # You could add more fields here if needed # Node that receives InputState and returns a Command def some_node(input: InputState) -> Command[Literal["other_node"]]: print(f"Called some_node with input field value: {input.some_field}") # Return a Command that updates the state and specifies where to go next return Command( update={"some_field": input.some_field + 1}, goto="other_node" if input.some_field < 5 else END ) # Another node that uses the updated state def other_node(state: OverallState) -> OverallState: print(f"Called other_node with state field value: {state['some_field']}") # You can do additional processing here return {"some_field": state["some_field"] + 2} # Create the workflow workflow = StateGraph(OverallState, input=InputState) workflow.set_entry_point("node") workflow.add_node("node", some_node) workflow.add_node("other_node", other_node) # Instead of using conditional edges, we're using Command to control flow workflow.add_edge("other_node", "node") # Compile the graph compiled_workflow = workflow.compile() ``` Try to complie it using this and it will has no problem ``` # Test the workflow result = compiled_workflow.invoke(InputState(some_field=1)) print("Final result:", result) # Test with different input result2 = compiled_workflow.invoke(InputState(some_field=5)) print("Second result:", result2) ```
Author
Owner

@vbarda commented on GitHub (Mar 11, 2025):

@sand-heap this is fixed in 0.3.6 - thanks for your patience! the input schema annotation in the conditional edge function is now respected and acts as an input "filter": if the input schema annotation is provided, the conditional edge function will read only the keys in the schema regardless of the graph state it receives. if you want it to read full graph state, simply remove schema annotation or use something like dict for annotation

@vbarda commented on GitHub (Mar 11, 2025): @sand-heap this is fixed in 0.3.6 - thanks for your patience! the input schema annotation in the conditional edge function is now respected and acts as an input "filter": if the input schema annotation is provided, the conditional edge function will read only the keys in the schema regardless of the graph state it receives. if you want it to read full graph state, simply remove schema annotation or use something like `dict ` for annotation
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langgraph#465