Conditional edges and routing

LangGraph · Essentials · 10 min

Branching and looping are what make a graph more than a fixed pipeline.

A normal edge always goes to the same node. A conditional edge calls a routing function after a node finishes; the function looks at the state and returns the name of the next node.

def route(state: State) -> Literal["refund_flow", "faq"]:
    return "refund_flow" if state["category"] == "billing" else "faq"

builder.add_conditional_edges("classify", route)

Literal[...] in the return type lists the possible answers. LangGraph uses it to know where the arrows go (for drawing and validation). Instead, you can pass the options explicitly: add_conditional_edges("classify", route, ["refund_flow", "faq"]), or a dict mapping return values to node names.

A router may return END to stop, or the name of an earlier node to form a loop. Loops are how agents work: call the model, run tools, go back to the model, until a condition says stop.

Every loop needs an exit condition. As a safety net LangGraph counts super-steps (rounds of node execution) and raises GraphRecursionError when a run exceeds recursion_limit. Since LangGraph 1.0.6 the default is 1000. Set a tighter limit per run with graph.invoke(inputs, {"recursion_limit": 25}); note it goes at the top level of the config, not inside configurable.

Keep routing functions pure: read the state, return a name, no side effects. Do the work in nodes.

Example

from typing import Literal, TypedDict
from langgraph.graph import StateGraph, START, END


class State(TypedDict):
    draft: str
    revisions: int


def write(state: State) -> dict:
    n = state["revisions"] + 1
    return {"draft": f"draft v{n}", "revisions": n}


def good_enough(state: State) -> Literal["write", "__end__"]:
    return END if state["revisions"] >= 3 else "write"   # loop back or stop


b = StateGraph(State)
b.add_node(write)
b.add_edge(START, "write")
b.add_conditional_edges("write", good_enough)
graph = b.compile()

print(graph.invoke({"draft": "", "revisions": 0}, {"recursion_limit": 10}))
# {'draft': 'draft v3', 'revisions': 3}

Remember

  • add_conditional_edges(source, router) picks the next node at run time
  • Declare possible targets with Literal or an explicit list/dict
  • Return END to stop or an earlier node to loop
  • recursion_limit (default 1000) guards against endless loops

Check yourself

  1. Where does recursion_limit go when invoking?
  2. A routing function returns the name of a node that already ran. What happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.langchain.com/oss/python/langgraph/graph-api