← failproof.ai
how-to·updated jul 2026·7 min read

ai agent stuck in a loop

your agent is repeating itself and going nowhere - the same edit, the same command, the same failing tool call, over and over. here is why coding agents loop, how to detect it, and the hook-level policy that breaks the loop, summarizes what got done, and restarts clean - across Claude Code, Cursor, Codex, Gemini CLI, and the rest.

What an agent loop actually is

A loop is the agent doing the same thing and expecting a different result. A test fails, the agent edits the same function, the test fails the same way, the agent edits it again. Or it runs npm install, hits the same error, reads the same file, and runs npm install again. Nothing crashes - every command exits, every tool call returns - so from the outside the run looks busy and healthy. It is just busy going nowhere.

The reason this is so common is structural. The model decides one turn at a time, and each turn is dominated by the most recent error. It does not have a clean view of the fact that this is the seventh time it has tried the identical fix - that pattern is spread across turns, and the thing best placed to see it is not the model reasoning from inside the loop. It is a watcher outside the loop, counting.

How to detect the loop

You do not need the model to notice it is stuck. You need to watch the run and count repetition. The signals are concrete:

  • The same tool call repeats. Identical (or near-identical) command, same arguments, same target file, N times in a row.
  • The same edit reverts and reapplies. The agent writes a line, something fails, it writes almost the same line again - the diff oscillates instead of converging.
  • The same error text recurs. The tool result carries the same failure message on every pass; the state is not changing.
  • Call count climbs, progress does not. Fifty model calls into a two-call task, with no new files touched and no test flipping green.

Any one of these is a threshold you can trip on. The moment the count crosses the line, you act - before the loop spends another twenty calls proving the same point.

How to break it at the hook layer

The place to intervene is the harness hook layer - the PreToolUse point every modern coding agent exposes, where a policy runs beforethe next action and can allow it, deny it, or feed the agent a message. failproof plugs in there and keeps a short memory of recent calls, so it can see the repetition the model can't.

When the loop policy trips, it does three things in order:

  • Break the loop. Deny the repeated action so the agent physically cannot run it a seventh time.
  • Summarize where it got to. Collapse the wasted turns into a short note - what was tried, what kept failing, what actually changed - so nothing learned is lost.
  • Restart fresh. Hand that summary back so the agent starts from a clean frame with the dead end marked, or stop and surface it to you if the same wall gets hit again.

None of this touches the agent's code or its prompt. The policy runs in a separate local process, reads the trace, and enforces at the hook - so it behaves identically whether you're driving Claude Code, Cursor, Codex, or opencode. This is the same hook-level enforcement that catches every other failure mode; the loop is just one policy of the thirty-nine built in.

Concretely, the policy is a few lines. It runs at the PreToolUse hook, keeps a short memory of recent calls, and denies the one that keeps repeating:

// break-loop.js — deny a call that keeps repeating with no progress
import { customPolicies, allow, deny } from "failproofai";

const seen = [];

customPolicies.add({
  name: "break-loop",
  description: "Stop the agent repeating the same call in a loop",
  match: { events: ["PreToolUse"] },
  fn: async (ctx) => {
    const sig = JSON.stringify([ctx.toolName, ctx.toolInput]);
    seen.push(sig);
    if (seen.filter((s) => s === sig).length >= 3) {
      seen.length = 0;
      return deny(
        "This exact call ran 3x with no progress. Stop, summarize " +
        "what you tried, and take a different approach."
      );
    }
    return allow();
  },
});

Install it with failproofai policies --install --custom ./break-loop.js, or just run failproofai policies --install to enable the built-in loop breaker along with the rest of the 39.

Find it once, stop it forever

Breaking a live loop is the fix half. The find half is making sure you catch the pattern the first time and every time after. agenteye records every run, so a loop shows up as a spike in call count with no matching progress - and you can replay the session span by span to see the exact call it got stuck on. Confirm the pattern in the trace, tune the threshold, and the policy catches it automatically on the next run. Find in observability, fix in policy.

Get started

failproof is free and open-source and runs entirely on your machine. Install the CLI, enable the built-in policies, and the loop breaker is live at the hook layer.

book a demo →