07 Oct LangGraph Loops and Cycles: Retry Logic and Iterative Workflows
This is where LangGraph pulls ahead of simple chains. A cycle sends the graph back to an earlier node, so it can retry, refine, or repeat until a goal is met.
The idea
+--------------+
v |
START -> increment -> check --(done)--> END
|
(again)
A loop is just a conditional edge (Chapter 6) that points backward.
Example 1: A simple counter loop
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
count: int
def increment(state: State):
return {"count": state["count"] + 1}
def check(state: State):
if state["count"] < 3:
return "again"
return "done"
builder = StateGraph(State)
builder.add_node("increment", increment)
builder.add_edge(START, "increment")
builder.add_conditional_edges(
"increment",
check,
{"again": "increment", "done": END},
)
graph = builder.compile()
for step in graph.stream({"count": 0}, stream_mode="updates"):
print(step)
Output:
{'increment': {'count': 1}}
{'increment': {'count': 2}}
{'increment': {'count': 3}}
How it works:
- increment runs and raises the count
- check reads the count. Below 3, it returns “again”, which loops back to increment
- At 3, it returns “done”, and the graph ends
Example 2: Retry logic
A realistic use: try a task, and retry if it fails, with a maximum number of attempts. We will simulate a task that succeeds on the third try.
import operator
from typing import Annotated, TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
attempts: int
success: bool
log: Annotated[list[str], operator.add]
def try_task(state: State):
attempt = state["attempts"] + 1
success = attempt >= 3 # pretend it works on attempt 3
result = "success" if success else "failed"
return {
"attempts": attempt,
"success": success,
"log": [f"Attempt {attempt}: {result}"],
}
def check(state: State):
if state["success"]:
return "done"
if state["attempts"] >= 5:
return "give_up"
return "retry"
builder = StateGraph(State)
builder.add_node("try_task", try_task)
builder.add_edge(START, "try_task")
builder.add_conditional_edges(
"try_task",
check,
{"retry": "try_task", "done": END, "give_up": END},
)
graph = builder.compile()
result = graph.invoke({"attempts": 0, "success": False, "log": []})
print(result)
Output:
{'attempts': 3, 'success': True, 'log': ['Attempt 1: failed', 'Attempt 2: failed', 'Attempt 3: success']}
What to notice:
- The log uses the operator.add reducer from Chapter 5, so each attempt adds to the history
- The router has three outcomes: retry, done, and give up
- The attempts >= 5 check is a safety exit. Every loop needs one.
The recursion limit: your safety net
What if your loop never ends? LangGraph stops it after 25 steps by default and raises an error. You can set your own limit:
from langgraph.errors import GraphRecursionError
try:
graph.invoke(
{"attempts": 0, "success": False, "log": []},
{"recursion_limit": 2},
)
except GraphRecursionError:
print("Loop stopped: too many steps")
Output:
Loop stopped: too many steps
Each node run counts as one step, and here the limit of 2 is reached before the task succeeds. Treat the limit as a backup, not your main plan. Your own exit condition (like attempts >= 5) should stop the loop first.
Where loops are used in real AI apps
- Retry: call an API until it works
- Self-correction: write an answer, review it, and rewrite if it is weak
- Agent loop: think, use a tool, think again (Chapter 11)
- Human feedback: revise a draft until it is approved
Common mistakes
- No exit condition: the loop runs until the recursion limit crashes it
- Exit condition never becomes true: for example, forgetting to update the counter inside the node
- Overwriting the log: without a reducer, each pass erases the history
- Setting the recursion limit too low: a healthy workflow gets cut off
Summary
- A cycle is a conditional edge that routes back to an earlier node
- Always include an exit condition, ideally with a maximum attempts check
- Reducers let you keep a history across loop passes
- recursion_limit protects you from infinite loops, and raises GraphRecursionError
Practice
- Change Example 2 so the task succeeds on attempt 6, and confirm the graph gives up after 5 attempts
- Add a delay field that doubles on each retry (1, 2, 4 and so on) to simulate backoff
- Build a loop that keeps adding numbers to a list until the total passes 50
If you liked the tutorial, spread the word and share the link and our website, Studyopedia, with others.
For Videos, Join Our YouTube Channel:Â Join Now
Read More:
- Generative AI Tutorial
- AI Ethics
- Machine Learning Tutorial
- Deep Learning Tutorial
- Ollama Tutorial
- Retrieval Augmented Generation (RAG) Tutorial
- ChatGPT Tutorial
- Microsoft Copilot Tutorial
No Comments