Imagine you hire a sous chef and give them one instruction: "keep tasting the soup until it's perfect, then stop." Simple enough. But this particular chef has no working sense of taste. They can stir, they can adjust, they can add salt, but they genuinely cannot tell good soup from bad. So they taste, shrug, adjust, taste again, shrug, adjust again. The kitchen fills up. Dinner service passes. Nobody comes in to say "stop, that's good enough." The chef just keeps going.
This is what happens when an AI agent runs without an external step limit. The agent has a task and a completion check: "keep running until done." But "done" is defined by the model itself, which may return False for every iteration if the task is underspecified, the completion criterion is broken, or the world state never reaches the expected condition. The agent doesn't hang or error out. It just loops, consuming compute and racking up API costs, until something outside of it intervenes.
The trap is that an agent loop looks like deliberate reasoning. Each iteration has a plausible action. Each completion check has a plausible rationale. The problem only becomes visible from outside: the number of steps keeps climbing and no final answer ever arrives.
That's an infinite agent loop: a control flow whose exit condition is determined by the agent's own judgment of the world state, with no hard external ceiling on how many times it can try. Judgment is not a guarantee. A bounded counter is.
while not agent.is_done(state): # agent decides when to stop
state = agent.step(state) # no ceiling on how many times
# 1 step, 10 steps, 10,000 steps: all indistinguishable from outside
No exception. No timeout at the language level. The function just never returns until your cloud bill does it for you.
An agent is a program that runs in a loop: observe the world state, decide on an action, execute it, check whether the task is complete, and repeat. This pattern is called a ReAct loop (Reason, Act) in most AI agent frameworks. It's powerful because the agent can adapt its actions based on what happened last time. It's dangerous because the agent decides when it's done, and that decision can be wrong indefinitely.
The loop above describes an agent operating correctly: each iteration makes progress, and eventually the check returns True. What it doesn't show is what happens when the check never returns True, or when the agent's actions make no net progress, or when the task goal is ambiguous enough that "done" is never reached.
The stop condition is inside the agent's own judgment. Unlike a for loop with a fixed range, an agent loop's exit condition is evaluated by the model at runtime: "do I think this task is complete?" That question is answered by the same system that just spent the last N steps convincing itself it's making progress. If the task is ill-defined, or the completion check is buggy, or the world state never converges to the expected pattern, the model's judgment will be "no, not done yet" on every iteration, forever.
Each iteration looks intentional from the inside. This is what makes the loop hard to detect. The agent isn't spinning in place: it's generating plausible-sounding reasoning, calling real tools, reading real output. Step 47 looks as deliberate as step 2. There's no stack overflow, no exception, no sentinel value changing. From inside the loop, everything is working as designed. From outside, the task simply never finishes.
The cost compounds linearly. Every agent step calls a language model. Most frameworks also call external tools (search APIs, code executors, file systems). Each step costs real money and real time. An agent that runs 500 steps instead of 10 costs 50x more. An agent that runs indefinitely costs until someone notices, which in production might be a budget alert three days later. The runaway loop is also a billing incident.
The second failure mode is subtler: a max_steps guard exists in the code, but a retry-on-error block inside the loop resets the counter whenever the model throws a transient exception. So the ceiling looks enforced but isn't. A flaky API that errors every 15 steps allows the counter to reset indefinitely, producing an effectively infinite loop even in code that appears to have a bound.
Don't take our word for it. Step through a live agent loop and watch where it gets stuck.
Configure the agent and step through its loop one iteration at a time. Watch what happens when the stop condition never returns True and no external limit exists.
Run the "Ambiguous" or "Broken check" task without an external limit until you see it loop past 10 steps to unlock the problem stage.