Loops and the recursion limit
An edge can point back to an earlier node, which makes the graph loop. LangGraph caps a run at a set number of steps and raises GraphRecursionError if a loop never ends. The default is large (over ten thousand in LangGraph 1.2), so you usually set recursion_limit yourself.
Last updated: 27 Sep, 2026 · LangGraph 1.2
Looping is how an agent repeats until a job is done: draft, check, redraft. A conditional edge back to the start keeps it going; the same edge returning END stops it. The limit is a safety net for loops that never stop.
Imports and the state
Start with the imports and a state holding one counter. GraphRecursionError is the error LangGraph raises when a run takes too many steps.
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START
from langgraph.errors import GraphRecursionError
class State(TypedDict):
n: int # a counter the loop keeps raisingThe self-looping node
Build a graph with one node whose edge points back to itself. Nothing ever sends the run to END, so it repeats without stopping.
builder = StateGraph(State)
builder.add_node("loop", lambda s: {"n": s["n"] + 1}) # add 1 each time
builder.add_edge(START, "loop")
builder.add_edge("loop", "loop") # edge back to itself: never endsCatching the limit
Run it inside a try, with a low recursion_limit so the cap is reached at once. LangGraph stops the run and raises GraphRecursionError, which the except catches.
try:
builder.compile().invoke({"n": 0}, {"recursion_limit": 5}) # a low cap to hit it fast
except GraphRecursionError:
print("hit limit")The unbounded loop end to end
The same pieces in one file, ready to run.
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START
from langgraph.errors import GraphRecursionError
class State(TypedDict):
n: int
builder = StateGraph(State)
builder.add_node("loop", lambda s: {"n": s["n"] + 1})
builder.add_edge(START, "loop")
builder.add_edge("loop", "loop") # points back to itself
try:
builder.compile().invoke({"n": 0}, {"recursion_limit": 5})
except GraphRecursionError:
print("hit limit")Why the loop hit the limit
- The edge
loop → loopsends the run back to the same node forever. - Nothing ever ends the loop, so LangGraph stops it at the recursion_limit and raises
GraphRecursionError. - A real loop ends itself with a conditional edge that returns
ENDonce the work is done.
A loop that ends itself
A real loop uses a conditional edge that returns END once a stop condition is met. Here it stops when n reaches 5, so there is no error.
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
n: int
def step(s): return {"n": s["n"] + 1}
def keep_going(s): return "step" if s["n"] < 5 else END # loop back, or stop
b = StateGraph(State)
b.add_node("step", step)
b.add_edge(START, "step")
b.add_conditional_edges("step", keep_going) # the edge that can end the loop
print(b.compile().invoke({"n": 0})["n"])Raising the limit
graph.invoke({"n": 0}, {"recursion_limit": 100}) # top-level config keyrecursion_limit is a top-level config key, not inside configurable.Where loops appear
- The agent loop: call the model, run a tool, call the model again, until it stops asking for tools.
- Retry-until-valid: redraft until a check passes.
Related
- Previous: Command: update and route in one step
- Next: Send: run one node per item
- Reference: GraphRecursionError
- Add a conditional edge from
loopthat returnsENDoncenreaches 5. Does the error go away? - Set
recursion_limitto 3 and watch it stop sooner.
Little by little, you're building something great.