Conditional edges: choosing a path
A conditional edge lets the graph look at the state and choose which node runs next: add_conditional_edges(source, router, targets) calls your router function and goes where it returns.
Last updated: 27 Sep, 2026 · LangGraph 1.2
So far every run goes the same way. But a billing complaint and a crash report should not get the same reply, and junk mail should get none at all. That is three outcomes from one starting point.
The add_conditional_edges call
def router(state):
return "node_name" # a node name to go there, or END to stop
builder.add_conditional_edges(source, router, ["node_a", "node_b", END])The state
Start with the imports and the state. The state holds the incoming ticket and the reply a node will write back.
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
ticket: str # the incoming message
reply: str # the answer a node writesThe handler nodes
Add two handler nodes, one per team. Each returns a fixed reply for now.
def billing(state):
return {"reply": "We will refund the double charge."}
def technical(state):
return {"reply": "Try logging out and back in."}The router function
Here is the new idea. pick_team reads the ticket and returns where to go next: a node name, or END to stop. It is a plain function, not a node, so it is never added with add_node.
def pick_team(state):
if "win a prize" in state["ticket"]: # junk mail
return END # stop, no reply
# a charge goes to billing, anything else to technical
return "billing" if "charge" in state["ticket"] else "technical"Wiring the router
Wire the two nodes, then attach the router to START with add_conditional_edges. The list at the end names every place the router might send the run.
builder = StateGraph(State)
builder.add_node("billing", billing)
builder.add_node("technical", technical)
builder.add_conditional_edges(START, pick_team, ["billing", "technical", END]) # router picks the path
graph = builder.compile()Running three tickets
Run three tickets through the same graph and print each reply.
for ticket in ["charged twice", "app will not open", "win a prize now"]:
print(repr(graph.invoke({"ticket": ticket, "reply": ""})["reply"]))Three tickets, three paths
The same pieces in one file, ready to run.
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
ticket: str
reply: str
def billing(state):
return {"reply": "We will refund the double charge."}
def technical(state):
return {"reply": "Try logging out and back in."}
def pick_team(state):
if "win a prize" in state["ticket"]:
return END
return "billing" if "charge" in state["ticket"] else "technical"
builder = StateGraph(State)
builder.add_node("billing", billing)
builder.add_node("technical", technical)
builder.add_conditional_edges(START, pick_team, ["billing", "technical", END])
graph = builder.compile()
for ticket in ["charged twice", "app will not open", "win a prize now"]:
print(repr(graph.invoke({"ticket": ticket, "reply": ""})["reply"]))Why each ticket went where it did
pick_teamis a function but not a node: it is never added withadd_node, and it returns a destination, not a state update.- Returning a string sends the run to the node with that name; returning
ENDstops it. - The third ticket gets an empty reply because
pick_teamsent it straight toENDand neither reply node ran.
What the router may return
| Router returns | What happens |
|---|---|
"billing" | The billing node runs next |
END | The run stops with no further node |
A value in the path_map | Maps to the node the map names |
True / the ticket | Error, unless it is a key in the path_map |
When to branch on the state
- Routing a request by its intent (billing vs technical vs spam).
- Skipping steps that do not apply, or ending a run early.
END, or a key present in the optional path_map. Returning the ticket or True with no map is the first mistake.Related
- Previous: draw_mermaid: seeing your graph
- Next: Reducers: combining values in the state
- Reference: Graph API: conditional edges
- Add a
generalnode and route tickets that match neither rule to it. - Make
pick_teamreturnTruewith no path_map and read the error. - Route a fourth ticket, "refund my charge", and predict the path before running.
You understood something today that you didn't yesterday.