When AI agents get stuck in loops

The same tool call, the same observation, the same retry — until cost and latency run away. How agent loops start, how to detect them, and how to stop them before they reach a user.

Comlabs Technologies Pvt Ltd6 min read
When AI agents get stuck in loops

Most agent demos look linear. A request comes in, the model thinks, a tool runs, an answer comes back. In production the path is rarely that clean. The model calls a tool, reads an observation that does not quite settle the question, calls the same tool again, and then again. Tokens keep moving. The user waits. The bill does not.

That pattern is agent looping. It is not a dramatic failure. It is a quiet one: the system is busy, confident, and going nowhere.

What a loop actually is

A loop is any stretch of an agent run where the state of the world does not change, but the agent keeps acting as if it might. The usual shapes:

  • Tool-call cycles. The same function, with the same arguments, returns the same payload. The model treats the repeat as new information.
  • Planner–executor ping-pong. A planner emits a step. An executor reports it could not finish. The planner emits the same step.
  • Retry storms. A flaky tool, a timeout, or a vague error string is read as “try again,” without a backoff, a budget, or a different strategy.
  • Context amnesia. Earlier in the window the agent already learned the file does not exist, the API rejected the key, or the user said stop. That turn has scrolled out of attention, so the agent rediscovers the problem from scratch.

None of these require a malicious model. They show up in ordinary ReAct-style agents the moment the environment is messy — which is the only environment that matters.

If the observation did not change the state, another call is not progress. It is motion.

Why agents loop

Language models are trained to continue. An agent runtime that only says “keep going until you are done” is asking a continuation engine to invent a stopping rule. It will often invent the wrong one.

Four conditions make loops likely:

  1. Success is vague. “Find the answer,” “fix the issue,” “research this” have no checkable done-state. The model cannot tell finished from almost.
  2. Tools are chatty but not decisive. Search, scrape, and list operations return text that looks useful. They rarely return a structured signal that the question is closed.
  3. Errors are under-specified. “Failed,” “timeout,” and “not found” all invite a retry. They do not say whether retrying with the same input can ever work.
  4. There is no memory of action. If the runtime does not hash recent tool calls, the model has to notice the repeat itself. Under pressure, it usually does not.

Add a generous step limit — or none — and a loop is not a bug. It is the default.

How it shows up in a product

Inside the lab, a loop looks like a long trace. In a product it looks like:

  • A spinner that outlives the user’s patience.
  • A reply that restates the last three tool results and then asks a question the user already answered.
  • A cost spike that does not correspond to a better outcome.
  • A support ticket that says the assistant “got stuck thinking.”

Teams often respond by prompting harder: “Do not repeat yourself. If a tool failed, try something else.” That helps until the next ambiguous observation. Prompting is not a control loop. The runtime has to own termination.

Detect the loop before the twentieth step

You do not need a research paper to catch most loops. You need a fingerprint.

Hash the last N tool calls as (name, canonical arguments, observation digest). If the same fingerprint appears twice in a short window, the agent is not exploring. It is orbiting. At that point the runtime should stop proposing the same action and either:

  • force a different tool or a different argument set,
  • ask the user one precise question, or
  • end the run with what is already known.

Pair that with hard budgets that the model cannot negotiate away:

  • a maximum number of steps per request,
  • a maximum number of calls per tool,
  • a wall-clock budget,
  • a token or cost ceiling.

When a budget trips, the user should see a finished thought — “I tried X and Y; here is what I know; here is what I still need” — not a truncated inner monologue.

Design for stopping, not just acting

The agents that behave in production are usually less free than the ones in demos. They look more like state machines with an LLM inside a step, and less like an unbounded chat with tools attached.

Practices that hold up:

Write the done-state first. Before the agent runs, define what “finished” looks like in data: a record created, a file patched, a citation list of length N, a form the user confirmed. If you cannot name it, the model cannot hit it.

Make tools idempotent and typed. A tool that returns { status: "unchanged", reason: "already applied" } is worth more than a paragraph of logs. The model can stop. The runtime can stop even if the model does not.

Separate retrieve from decide. Search and fetch belong in a bounded gather phase. Synthesis belongs after. Mixing them in one ReAct loop is how “one more search” becomes twelve.

Put a human on the expensive edges. Payments, destructive edits, outbound messages, and anything irreversible should require a checkpoint. A loop that cannot spend or delete is a cheaper loop.

Keep a working scratchpad the model cannot rewrite. Tool results, user constraints, and rejected plans should live in structured state, not only in the chat transcript. Transcripts drift. State does not, if you refuse to let the model clobber it.

What we watch when we ship agentic work

When Comlabs puts an agent behind a real workflow — onboarding, operations, research, support — the trace is part of the product, not an afterthought. We look for:

  • repeat fingerprints per session,
  • steps that consume tokens without changing state,
  • tools that account for most of the runtime but little of the outcome,
  • runs that hit the budget versus runs that reach the done-state.

Those numbers tell you whether the agent is working or merely occupied. Occupied is easy to ship. Working takes a stop condition.

Loops will not disappear. Models will keep trying one more time. The job of the system around them is to notice the orbit, close it, and hand a person something they can use.

AgentsProduct engineeringStudio notes

Let's build something

Have a looping workflow to untangle?

We design and engineer product software with stop conditions, budgets, and traces you can actually read.

Start a conversation