The first sign is usually innocent.
One agent calls another. That agent checks the result, sends it back for revision, and the first agent asks for another revision. Then it happens again.
And again.
Your terminal keeps printing nearly identical messages. API usage climbs. The workflow never reaches the final node.
You stop it manually.
Welcome to one of the most frustrating problems in multi-agent development: the infinite loop.
These failures are rarely caused by one obviously broken line of code. More often, the workflow has no reliable definition of “done.” A reviewer keeps rejecting an answer. A router keeps selecting the same node. A tool keeps returning a result that triggers the same action. Or two agents keep handing work back and forth because neither one has enough information to make a final decision.
The good news is that these problems are usually fixable.
The trick is to stop treating agents as independent chatbots and start treating the entire system as a state machine with explicit execution boundaries.
Why Multi-Agent Workflows Get Stuck
A traditional Python function normally has a fairly predictable lifecycle:
input → function → output
A multi-agent workflow looks more like this:
User
↓
Planner
↓
Researcher
↓
Writer
↓
Reviewer
↓
Planner
↓
Researcher
↓
...
That final arrow is where things can go wrong.
The problem isn’t necessarily that an agent is “confused.” The workflow may simply be giving it permission to continue.
This distinction matters.
Suppose you have three agents:
- Researcher gathers information.
- Writer produces a draft.
- Reviewer checks the draft.
A perfectly reasonable design might be:
Researcher → Writer → Reviewer
↓
Needs changes?
/ \
Yes No
↓ ↓
Writer END
But an unsafe design might look like:
Writer → Reviewer
↑ ↓
└────────┘
There is technically a loop.
That’s not automatically bad. Iteration is often exactly what you want.
The problem appears when there is no reliable condition capable of breaking that loop.
Human-in-the-Loop for AI Workflows: How to Keep Control Without Losing Automation Speed
The Three Most Common Causes of Infinite Agent Loops
Most looping problems fall into a few recognizable categories.
1. There is no termination condition
This is the obvious one.
The workflow says:
“Keep reviewing until the output is good.”
But what does “good” mean?
If the reviewer is another LLM, it might decide that something can always be improved.
You can end up with:
draft → review → revise → review → revise → review
Nothing is technically broken.
The system is simply doing exactly what you asked.
2. The termination condition never becomes true
This one is sneakier.
You might have a condition such as:
if state["score"] >= 0.9:
return END
Looks fine.
But perhaps the score is calculated incorrectly, never updated, or stored under a different state key.
The graph has an exit.
It just never reaches it.
3. The router keeps selecting the same node
Consider a routing function:
def route(state):
if state["approved"]:
return "finish"
return "review"
If approved never changes from False, the router will continuously send execution back to review.
This becomes especially common when the state is updated by several agents and one agent accidentally overwrites information produced by another.
Debugging Infinite Loops in LangGraph
LangGraph makes loops explicit, which is both a strength and a responsibility.
You define nodes, edges, state transitions, and conditional routing. That gives you a lot of control—but it also means you need to understand exactly how execution moves through the graph.
LangGraph’s documentation describes loops as requiring a mechanism for termination, commonly a conditional edge that eventually routes to END.
A simplified workflow might look like this:
from langgraph.graph import StateGraph, START, END
builder = StateGraph(State)
builder.add_node("writer", writer)
builder.add_node("reviewer", reviewer)
builder.add_edge(START, "writer")
builder.add_conditional_edges(
"writer",
should_review,
{
"review": "reviewer",
"done": END
}
)
builder.add_edge("reviewer", "writer")
The dangerous part is the final relationship:
writer → reviewer → writer
That’s a valid graph.
But you need something that eventually changes the routing decision.
Debugging AI Agents: What to Do When Your Multi-Agent System Gets Stuck in an Infinite Loop 2026
Use an Explicit Iteration Counter
One of the simplest fixes is to track the number of attempts.
For example:
class State(TypedDict):
draft: str
review: str
iterations: int
Then your routing logic can enforce a hard limit:
def should_continue(state):
if state["iterations"] >= 3:
return "done"
if state["review"] == "approved":
return "done"
return "review"
Now the workflow has two ways out:
- The reviewer approves the result.
- The maximum number of iterations is reached.
That second condition is extremely important.
LLMs are probabilistic. Your workflow shouldn’t depend on an LLM eventually making the “correct” decision.
Don’t Confuse Recursion Limits With Good Workflow Design
LangGraph also provides a recursion limit.
If the graph reaches the configured maximum number of steps, LangGraph can raise GraphRecursionError. The framework documents this specifically as protection against graphs that continue executing without reaching a stop condition.
For example:
try:
result = graph.invoke(
{"draft": initial_draft},
{"recursion_limit": 50}
)
except GraphRecursionError:
print("Workflow exceeded execution limit")
This is useful.
But don’t make the mistake of thinking:
“I increased the recursion limit, so the loop is fixed.”
You haven’t fixed the loop.
You’ve simply allowed it to run longer.
If your workflow normally needs 8 steps and suddenly requires a recursion limit of 500, that’s a debugging signal.
Investigate the graph.
A Better LangGraph Loop

A safer architecture combines a meaningful termination condition with a maximum number of attempts.
def route_after_review(state):
if state["approved"]:
return "finish"
if state["iterations"] >= 3:
return "finish"
return "rewrite"
The resulting flow becomes:
┌──────────────┐
│ Writer │
└──────┬───────┘
↓
┌──────────────┐
│ Reviewer │
└──────┬───────┘
↓
┌─────────────────┐
│ Approved? │
└──────┬──────────┘
Yes │ No
│
┌─────┴──────┐
↓ ↓
END Iterations
↓
< max limit?
/ \
Yes No
↓ ↓
Writer END
This is much easier to reason about.
Debugging Infinite Loops in CrewAI
CrewAI approaches orchestration differently.
Instead of manually constructing every graph edge, you typically define agents, tasks, crews, processes, and flows.
That abstraction is convenient, but it can make repeated execution feel less obvious when something starts looping.
CrewAI’s current agent configuration includes execution controls such as max_iter, max_execution_time, and max_retry_limit. Its documentation lists max_iter as the maximum number of attempts an agent can make before providing its best answer.
A basic defensive configuration might look like:
from crewai import Agent
reviewer = Agent(
role="Content Reviewer",
goal="Check whether the article meets the quality requirements",
backstory="Experienced technical editor",
max_iter=5,
max_retry_limit=2,
verbose=True
)
The important part isn’t the exact number.
It’s the boundary.
Without execution limits, an agent can spend far too long reasoning, calling tools, retrying operations, or attempting to satisfy an objective that is poorly defined.
max_iter Is a Safety Net, Not a Cure
Suppose your reviewer has this goal:
Review the article and improve it until it is perfect.
That’s a dangerous instruction.
“Perfect” doesn’t have a measurable stopping point.
A better goal might be:
Review the article for factual errors, missing sections,
and unclear explanations. Return APPROVED when no blocking
issues remain.
Now the agent has a much clearer endpoint.
Even better, separate the decision from the editing.
Reviewer
↓
Structured decision
↓
APPROVED / REVISE
Instead of asking:
“What do you think?”
ask for something that your workflow can actually evaluate.
For example:
{
"approved": false,
"issues": [
"Missing explanation of recursion limits"
],
"severity": "medium"
}
Structured state is much easier to route than free-form prose.
The Reviewer-Writer Loop
This is probably the most common multi-agent loop you’ll encounter.
Imagine:
Writer → Reviewer → Writer → Reviewer
The reviewer says:
“Add more technical detail.”
The writer adds detail.
The reviewer responds:
“The explanation is now too long.”
The writer shortens it.
The reviewer then says:
“Some technical context was removed.”
And you’re back where you started.
The agents aren’t necessarily malfunctioning.
They’re optimizing different objectives.
The writer is trying to satisfy the writing task.
The reviewer is trying to satisfy the review criteria.
If those criteria aren’t formalized, they can fight forever.
Give the Reviewer a Finite Contract
Instead of:
Review the work and suggest improvements.
try something like:
Check the output against these five requirements:
1. Correct technical explanation
2. No missing required sections
3. Code examples are syntactically valid
4. No critical factual errors
5. Target length is between 1,500 and 2,500 words
Return APPROVED if all five requirements pass.
Otherwise return REVISE and list only the failed requirements.
That’s a much better control mechanism.
The reviewer has a finite checklist.
Your router can then act on the result.
Detecting a Loop Before It Becomes Expensive
You don’t want to discover an infinite loop after you’ve burned through thousands of API calls.
Logging is your friend.
For every execution, record at least:
run_id
node_name
iteration
timestamp
decision
tool_called
state_hash
A simple state hash can reveal something particularly useful.
If the workflow repeatedly produces the same state:
State A
State B
State C
State A
State B
State C
you probably have a cycle.
That is different from healthy iteration.
Healthy iteration should normally move toward a different state:
Draft v1
Draft v2
Draft v3
Approved
If you’re seeing:
Draft v1
Draft v1
Draft v1
Draft v1
something is wrong.
State Changes Matter More Than Agent Messages
When debugging a multi-agent workflow, don’t focus only on what the agents say.
Look at what changes.
This is a common mistake:
Reviewer says: "Needs revision."
Writer says: "I revised the article."
Reviewer says: "Needs revision."
The messages sound different.
But perhaps the actual state is identical.
For example:
{
"draft": "...same text...",
"approved": False
}
If the writer doesn’t actually modify the relevant state, the next reviewer receives essentially the same input.
And the loop continues.
A useful debugging question is:
What changed between iteration 4 and iteration 5?
If the answer is “nothing important,” you’ve probably found the problem.
CrewAI vs. LangGraph: Which Is Easier to Control?
Both frameworks can support iterative workflows, but they encourage different ways of thinking.
| Feature | CrewAI | LangGraph |
|---|---|---|
| Agent abstraction | Strong | More low-level |
| Explicit graph control | Moderate | Strong |
| State-machine style | Possible | Core design |
| Agent iteration limits | max_iter | Graph/config limits |
| Conditional routing | Flows/process logic | Conditional edges |
| Loop visibility | Depends on architecture | Very explicit |
| Best fit | Agent teams and task orchestration | Stateful workflows and complex control |
CrewAI’s current documentation focuses on agents, crews, flows, tasks, processes, guardrails, and observability as parts of its orchestration model.
LangGraph, meanwhile, is designed specifically around stateful agent orchestration and gives you direct control over nodes, edges, state, and execution limits.
Neither is automatically “better.”
If your workflow needs very explicit state transitions and complex branching, LangGraph can feel more natural.
If you’re primarily coordinating a team of specialized agents around tasks, CrewAI can provide a more convenient abstraction.
Five Practical Rules for Preventing Infinite Loops
After building enough agent workflows, a few rules become hard to ignore.
Rule 1: Every loop needs an exit
If you intentionally create:
A → B → A
document exactly how the system can reach:
A → END
Don’t rely on the model eventually deciding to stop.
Rule 2: Always have a maximum iteration count
Even if your logical termination condition is excellent, add a hard ceiling.
Something like:
MAX_REVISIONS = 3
can save you from an expensive production failure.
Rule 3: Make state transitions observable
Log:
node
iteration
decision
state changes
errors
tool calls
Without this information, debugging becomes guesswork.
Rule 4: Prefer structured decisions
Instead of:
"The article looks mostly good but perhaps could use..."
use:
{
"approved": false,
"reason": "Missing source verification"
}
Machines are much better at routing structured information.
Rule 5: Don’t solve infinite loops by simply raising the limit
If LangGraph throws a recursion error at 25 steps, changing it to 1,000 isn’t necessarily progress.
If CrewAI hits its iteration limit, increasing max_iter from 20 to 100 may simply make the failure more expensive.
First determine why the workflow hasn’t stopped.
Then decide whether the limit actually needs changing.
A Simple Debugging Checklist
When an agent workflow starts running forever, work through this list.
1. Identify the repeating nodes.
Is it:
Writer → Reviewer → Writer
or something more complicated?
2. Inspect the state.
Compare the state before and after each iteration.
3. Check the router.
What exact value determines the next node?
4. Verify that value actually changes.
A condition is useless if the state feeding it never updates.
5. Add a hard iteration limit.
Don’t wait until production to discover that one.
6. Check tool retries.
A tool failure combined with automatic retries can create another form of repeated execution.
7. Separate quality checks from rewriting.
The reviewer should decide whether the output passes. It shouldn’t endlessly redefine the standard.
8. Make failure explicit.
Sometimes the correct result isn’t “keep trying.”
It is:
FAILED_AFTER_3_ATTEMPTS
That’s a valid production outcome.
The Most Reliable Pattern: Progress + Limit + Exit
If I were building a new multi-agent workflow today, I’d want every iterative section to answer three questions:
Is the workflow making progress?
How many times is it allowed to try?
What happens if it still fails?
That gives you a simple pattern:
┌─────────────┐
│ Execute │
└──────┬──────┘
↓
┌─────────────┐
│ Progress? │
└──────┬──────┘
Yes │ No
│
┌──────┴──────┐
↓ ↓
Continue Count attempt
↓
Limit reached?
/ \
No Yes
↓ ↓
Execute FAIL/END
That structure works whether the underlying implementation uses CrewAI, LangGraph, or something else entirely.
The framework may change.
The engineering principle doesn’t.
When an Infinite Loop Is Actually Useful
There is one important nuance.
Not every loop is a bug.
Some agent systems genuinely need iterative behavior.
A research agent might:
Search → Evaluate → Search → Evaluate
A coding agent might:
Write → Test → Fix → Test
A planning agent might:
Plan → Critique → Revise → Validate
Those loops are useful because each cycle should produce measurable progress.
The goal isn’t to eliminate loops.
It’s to make loops bounded and observable.
That’s the difference between an iterative workflow and a runaway workflow.
Build Agents That Know When to Stop
Multi-agent systems become much easier to debug when “done” is treated as part of the architecture rather than something the LLM figures out on its own.
Define the success condition.
Track the state.
Count iterations.
Log transitions.
Set a hard execution boundary.
Then give the workflow a graceful failure path.
LangGraph provides explicit mechanisms such as conditional edges and recursion limits for controlling graph execution, while CrewAI provides agent-level execution controls such as max_iter and retry limits.
Those controls are not substitutes for good workflow design. They’re guardrails around it.
And that’s probably the most useful mindset to carry into your next multi-agent project:
Don’t ask an AI workflow to stop. Build it so that it can stop.
For implementation details, the official CrewAI documentation is the best place to check current agent, task, flow, and execution behavior. For LangGraph, the official graph API documentation covers conditional routing, loops, and recursion limits in detail. If you’re specifically troubleshooting a GRAPH_RECURSION_LIMIT error, the LangGraph troubleshooting guide is the relevant reference.


