Loops and 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 set recursion_limit yourself when you want a runaway loop caught early.
Last updated: 29 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")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. - Catching
GraphRecursionErroris how a script survives a runaway loop; without thetry, the error stops the script.
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"])5
Raising the limit
graph.invoke({"n": 0}, {"recursion_limit": 100}) # top-level config keyrecursion_limit sits at the top level of the config dictionary, not inside the configurable key you meet in the checkpointer lesson.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
- Next: Send
- Reference: GraphRecursionError
- In the loop that ends itself, change the stop condition to
n < 10and predict the printed number. - Replace the lambda with a function that prints
n, setrecursion_limitto 3, and count the prints before hit limit.
Little by little, you're building something great.