LangGraphLangGraph 1.2 · Python 3.10+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
38 small wins to finish your pathNext lesson →

Conditional edges

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: 29 Sep, 2026 · LangGraph 1.2

A router function: random_play · from the Getting Started With LangGraph For Building AI Agents, Tutorial 2 · 49:53 to 52:40

Choosing cricket or badminton with random_play

The getting-started video gives its start_play node two ways out, cricket or badminton, and lets a small function choose. random_play takes the state and is annotated to return Literal['cricket', 'badminton'], the names of the two nodes it can pick. Its logic is a coin toss: if random.random() is above 0.5 it returns "cricket", otherwise "badminton". LangGraph reads that Literal annotation to know where the run can go, which is why the video's call below has no list of targets and its drawing shows only two dashed edges.

Wiring the conditional edge and running the graph · from the Getting Started With LangGraph For Building AI Agents, Tutorial 2 · 55:39 to 60:38

The graph gets the three nodes, an edge from START to start_play, and add_conditional_edges("start_play", random_play), which calls random_play after start_play and goes to the node it names. Both game nodes go to END. The first compile fails with ValueError: Found edge starting at unknown node 'Badminton': the last edge was written with a capital B, and an edge must use the node's exact name. With "badminton" fixed, draw_mermaid_png shows the two conditional edges as dashed lines:

start_play goes to badminton or cricket on dashed conditional edges, and both go to __end__.

The whole example calls no model, so it runs with no key:

ExampleFrom the video
from typing_extensions import TypedDict

class State(TypedDict):
    graph_info:str

def start_play(state:State):
    print("Start Play node has been called")
    return {"graph_info":state["graph_info"] + "I am planning to play"}

def cricket(state:State):
    print("Cricket node has been called")
    return {"graph_info":state["graph_info"] + " Cricket"}

def badminton(state:State):
    print("My badminton node has been called")
    return {"graph_info":state['graph_info'] + " Badminton"}

import random
from typing import Literal

def random_play(state:State)-> Literal['cricket','badminton']:
    if random.random()>0.5:
        return "cricket"
    else:
        return "badminton"

from langgraph.graph import StateGraph,START,END

## Build Graph
graph=StateGraph(State)

## add all the nodes
graph.add_node("start_play",start_play)
graph.add_node("cricket",cricket)
graph.add_node("badminton",badminton)

## Schedule the flow of the graph
graph.add_edge(START,"start_play")
graph.add_conditional_edges("start_play",random_play)
graph.add_edge("cricket",END)
graph.add_edge("badminton",END)

## Complie the graph
graph_builder=graph.compile()

print(graph_builder.invoke({"graph_info":"My name is Krish"}))

Two lines print from inside the nodes, then the final state. Which game comes up changes from run to run, because random_play tosses a coin each time; the video's run landed on badminton too. The missing space in "KrishI am" comes from start_play, which adds its words with no space in front. The video shows the picture with IPython's display, which works in a notebook; this script leaves that line out, and Graph visualization with draw_mermaid shows how to draw a graph from a script.

A support desk cannot route by coin toss: a billing complaint and a crash report need different replies, and junk mail needs none. The rest of this lesson routes tickets by what they say, with the same add_conditional_edges call.

The add_conditional_edges call

python
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.

python
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 writes

The handler nodes

Add two handler nodes, one per team. Each returns a fixed reply for now.

python
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

pick_team reads the ticket and returns where to go next: a node name, or END to stop.

python
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. In place of the list you can pass a dictionary, the path_map, that maps each value the router returns to a node name.

python
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.

python
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.

Example
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_team is a function but not a node: it is never added with add_node, and it returns a destination, not a state update.
  • Returning a string sends the run to the node with that name; returning END stops it.
  • The third ticket gets an empty reply because pick_team sent it straight to END and neither reply node ran.

What the router may return

Router returnsWhat happens
"billing"The billing node runs next
ENDThe run stops with no further node
A value in the path_mapMaps to the node the map names
True / the ticketError, 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.
Watch out. A router must return a string matching a node name, or END, or a key present in the optional path_map. Returning the ticket or True with no map is the first mistake.
Try it yourself
  • Add a general node and route tickets that match neither rule to it.
  • Make pick_team return True with no path_map and read the error.
  • Route a fourth ticket, "refund my charge", and predict the path before running.
  • In the video's example, change the last edge to graph.add_edge("Badminton", END) and read the error.

You understood something today that you didn't yesterday.