TL;DR: An AI agent infinite loop happens because something about the run has no checkable end: an undefined "done," a tool result the model misreads as failure, two tools disagreeing, or context truncation erasing what it already tried. A stop sequence will not fix this, and reasoning tokens bill against the same cap as visible output, so a stuck loop is expensive as well as slow.
What "Looping Forever" Actually Looks Like
Not every long-running agent is stuck. A bounded retry that backs off, tries a different approach, and eventually reports failure is working as intended, just slowly. The real signal of a loop is repetition with nothing learned: the same tool call, or the same pair of tool calls, running again with the same arguments and producing the same result, while nothing in the transcript shows the model noticing that this already happened.
That distinction matters because the fix is different in each case. A slow-but-converging agent needs patience or a smaller task. A looping one needs something added to the run that was never there: a way to check whether it is actually finished, a way to tell a real failure from a misread one, or a memory of what it already tried.
The Five Real Causes
| What you see | Real cause | The fix targets |
|---|---|---|
| The agent never stops, even after the task looks done | The instruction states a direction, not a checkable end state | An explicit, verifiable definition of done |
| The same tool call repeats with identical arguments | A tool result reads as failure, so the model retries the same way | A tool response the model can act on differently, or a cap on identical retries |
| The agent flips between two conclusions | Two tools return contradicting information and neither is authoritative | One declared source of truth, or a flag instead of a silent resolve |
| "Keep going until it's right," endlessly | Nobody stated what "right" means, so there is no exit condition to hit | Stated acceptance criteria before the run starts |
| The agent repeats work from ten steps ago | Context truncation dropped the record of what was already tried | A running log kept outside the model's working context |
Cause One: No Termination Condition, Only a Direction
A goal like "keep improving this until it's good" has no state where the model can say, with something to point to, that it is finished. Every draft could plausibly be a little better, so a directional instruction can always justify one more pass. This is the same failure that makes a model stop too early on an undefined task, just running in the opposite direction: instead of guessing a short "done" and quitting, it never finds a "done" to guess.
The fix replaces the direction with a condition someone else could check without reading the model's reasoning: a named file exists with the expected content, a test suite exits with status 0, every row in a tracked list has a non-empty status field. If a human would have to re-read the whole transcript to decide whether the task is finished, the model cannot reliably decide it either.
Cause Two: A Tool Result the Model Reads as Failure
Tool-calling agents, including ones built on the Model Context Protocol, are explicitly designed to retry after a bad result. The current MCP specification distinguishes protocol errors from tool execution errors, and says the latter should be surfaced to the model precisely because they "contain actionable feedback that language models can use to self-correct and retry with adjusted parameters". That is the intended behavior, and on a genuinely fixable problem, a wrong date format or an out-of-range value, it works: the model adjusts, retries, and moves on.
The loop happens when the underlying condition cannot actually be fixed by adjusting parameters, but the model keeps trying anyway, because nothing tells it the retry budget for this specific call is exhausted. A rate limit that will not clear for another ten minutes, a permission the agent will never be granted, or a malformed response the tool always returns for this input all look, from the model's side, like something one more attempt might fix.
Before retrying any tool call, compare it to your last 3 calls. If the
tool, arguments, and result are all identical to a previous attempt,
do not retry again. Instead, report the exact call and result as a
blocker, and either try a genuinely different approach or stop and
ask.
Cause Three: Two Tools That Disagree
An agent with more than one way to check a fact, a search tool and a file it already has locally, for instance, can end up alternating between them when they contradict each other. Each check looks locally reasonable: reconcile with the newer information, then reconcile back when the older source seems more authoritative for this specific case. Neither side of that oscillation is wrong on its own, which is exactly why it does not resolve itself.
The fix is deciding, in the instructions, which source wins when they conflict, or requiring the agent to surface the conflict explicitly instead of silently picking a side each time. A flagged contradiction that a person reviews is worth more than a plausible-looking answer that was actually the product of the agent flip-flopping for the last five turns.
Cause Four: "Keep Going Until Done" With No Definition of Done
This is cause one's sibling, and it deserves its own entry because it shows up specifically in multi-step, tool-using runs rather than single-answer drafts. "Fix the failing tests" sounds like a checkable target, but it silently assumes the model knows which tests, what "fixed" looks like if a test is flaky rather than broken, and whether changing the test itself counts. Without stating that up front, the agent can loop between "fix the code" and "fix the test" for a failure that is actually neither.
Definition of done for this run: the specific command `npm test`
exits with status 0, with zero test files modified. If a test looks
wrong rather than the code, stop and report which test and why,
instead of editing the test to pass.
Cause Five: Context Truncation Erasing the Record
Long-running agents accumulate a transcript that eventually exceeds what the model can hold in view, and older turns fall out of the working context to make room for newer ones. When that happens, the record of an already-attempted approach can disappear along with it, and the agent can rediscover the same dead end several turns later with no memory of having been there.
The fix is keeping a log of what has been tried somewhere the truncation cannot reach: a running file the agent appends to and re-reads, rather than relying on the conversation history alone to remember. This is the same discipline behind not letting AI touch code you didn't ask about: an explicit written record beats trusting the model to recall its own prior state.
Why a Loop Is Expensive, Not Just Slow
Reasoning models generate tokens you never see before producing a visible answer, and those tokens are not free or separately capped. OpenAI's API reference defines the max_output_tokens parameter directly: "An upper bound for the number of tokens that can be generated for a response, including visible output tokens and reasoning tokens." Its reasoning-models guide adds the failure mode in plain terms: exhausting that budget before any visible text is produced means "you could incur costs for input and reasoning tokens without receiving a visible response". In that case the response comes back with a status of incomplete.
Anthropic's current Thinking documentation states the same constraint for its own API: "Thinking tokens count toward max_tokens, so set it high enough to leave room for both the thinking and the response text." (This replaces the older, manual-budget "extended thinking" mode, which Claude 4.7 and later models reject outright with a 400 error.) Neither vendor gives reasoning a separate, uncapped budget.
This matters for loops specifically because a truncated, incomplete turn is not a clean stop, it is a partial one, and code that treats any non-success status as "try again" can turn one exhausted turn into another attempt at the same exhausted budget. Whatever hit the cap the first time is very likely to hit it again.
The Stop-Sequence Trap
"Just have it stop when it prints DONE" sounds like an obvious fix, and stop sequences are a real, documented parameter. They are also solving a different problem than an agent loop. A stop sequence halts token generation the instant a specified string appears, inside a single turn, before that turn even finishes. It says nothing about whether your code should start another turn afterward, which is the actual decision that needs to happen for a multi-step agent run.
It also does not work everywhere. OpenAI's own OpenAPI specification for the stop parameter states it directly: "Not supported with latest reasoning models o3 and o4-mini." The same spec caps it at up to 4 sequences on the models where it is supported at all. Google's Gemini API accepts up to 5. Anthropic's Messages API documents stop_sequences with no published count cap. None of the three variants gives your orchestrating code permission to keep the loop from starting its next turn; that decision has to live in the code that calls the model, not in a generation-time parameter.
The Guards That Actually Hold
Put together, the fixes above reduce to four habits that cover all five causes at once:
- A checkable definition of done, stated before the run starts, not a directional goal (causes one and four).
- A cap on identical retries, so a misread failure or a genuinely stuck condition cannot repeat past a small, fixed number of attempts (cause two).
- A declared source of truth for facts more than one tool can report, so contradiction becomes a flag instead of an oscillation (cause three).
- A running log outside the model's own context, so truncation cannot erase what was already tried (cause five).
This is a multi-step, tool-using run. Before your first action:
1. State the exact, checkable condition that means DONE.
2. You have a hard limit of 10 tool calls. State your count after each
one: "Call 6 of 10."
3. Before any tool call, check the log below. If this exact call and
result already appears, do not repeat it: report the blocker.
4. Append every tool call and result to the log, in order, and include
the full log in your final output.
None of this replaces a hard iteration cap enforced by the code that runs the loop, the same way --max-turns is enforced outside the model rather than trusted to the model's own counting. A prompt can make the model's reasoning easier to check. It cannot make the model self-enforce a limit it has every incentive, and occasionally every excuse, to miscount.
A clean stop, one that states what it checked and why it is actually finished, is the difference between an agent you can leave running and one you have to watch. If your process instructions have gaps like this elsewhere too, most of them share the same root: something that reads as clear to a person still leaves the model to guess, covered more generally in why AI ignores your format instructions and why AI ignores your word count. An agent loop is the same gap, just with tool calls in place of paragraphs.
Stop rewriting prompts. Start shipping.
Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.
Create An Account