You watch your Claude Code or Cursor agent run npm test for the twelfth time, edit the same expect(...).toBe(...) line, run again, fail again, revert. Or Aider ping-pongs between git diff and git restore. Tokens burn, the progress bar doesn’t move. 95% of the time the model isn’t “incapable of fixing this” — it’s stuck in a broken feedback loop: a flaky test, an under-constrained prompt, or context that no longer matches the code on disk. This article shows how to spot a loop in 30 seconds, kill it with one prompt, and prevent it from starting next time.
Common causes
Ordered by hit rate, highest first.
1. The test it keeps running is flaky
The single most common trigger. A test uses Date.now(), network calls, randomness, or snapshot timestamps, so the same code passes one run and fails the next. Agent sees red, edits, green, next run red — it thinks it broke its own fix, reverts, green again, edits again, red again.
Run 1: ✓ pass
Run 2: ✗ fail (snapshot mismatch — timestamp drift)
Run 3: ✓ pass (after agent "reverted")
Run 4: ✗ fail
How to spot it: Stop the agent and run the same command three times yourself without touching code. If you can reproduce green-red-green, the test is the problem, not the agent.
2. Ambiguous prompt — agent oscillates between interpretations
You said “fix the login bug,” but the codebase has both OAuth and magic-link paths. Agent fixes OAuth, magic-link test fails, agent switches to magic-link, OAuth test fails — it bounces between two mutually exclusive interpretations of the goal.
How to spot it: Look at the diffs of the last 5 actions. If they touch two different files in different functions and each new edit reverts the previous one, the prompt is under-constrained.
3. Plan is wrong but agent refuses to replan
Agent committed to “change schema → change API → change UI.” Step 1 is impossible (table already has data), but agent stays in step 1 rewriting migrations forever instead of revisiting the plan.
How to spot it: Ask the agent to print its current plan and which step it’s on. If it says “still on step 2” for more than 5 iterations, force a replan.
4. Context window saturated with old errors
In a long Claude Code or Codex session, 80% of the context is stale “previous failure” logs. The agent is fitting to those old errors and can’t see that the code has changed since.
How to spot it: Open a fresh session, paste the current code, restate the goal. If it succeeds on the first try, context pollution was the cause.
5. Agent ping-pongs between mock and real implementation
It writes a mock to make a test pass, next iteration decides the mock is unrealistic, edits the real implementation, original test breaks, goes back to the mock — loop.
How to spot it: Search the diff trail for jest.mock, vi.mock, MagicMock, or sinon.stub being added and removed across runs.
6. Tool call failed but agent didn’t notice
The Bash tool returned a non-zero exit code, but the agent parsed it as stdout and treated the command as “successful with weird output,” then ran it again.
How to spot it: Check the exit codes of the last 3 tool calls. If they’re non-zero but the agent never said “the command failed” in chat, it missed the failure.
Shortest path to fix
Ordered by ROI. Steps 1 and 2 break most loops in under a minute.
Step 1: Stop the agent, read the last 10 actions
Hit Esc (Claude Code) or click Stop (Cursor / Codex). Don’t let it keep burning tokens. Then ask in chat:
List the last 10 actions you took (file path + edit summary + command + result).
No commentary, just a table.
90% of the time you’ll see it bouncing between two files or editing the same line over and over.
Step 2: Break the loop with a one-sentence constraint
The most effective anti-loop prompt template:
Stop. Do not edit X anymore.
The real situation is Y.
Next, do only one thing: Z.
Do not touch any other file.
Concrete examples:
| Loop type | Constraint prompt |
|---|---|
| Edits test expectations repeatedly | ”Don’t touch the test. The test is right — change the implementation to match.” |
| Adds/removes mocks repeatedly | ”Keep the real implementation. Delete all mocks and run the integration test to see the real error.” |
| Rewrites migrations repeatedly | ”Schema is frozen. Restart the plan from step 1 but only touch the UI layer.” |
| Bouncing between two files | ”You may only edit src/auth/login.ts. All other files are read-only.” |
Step 3: Pin the test or success criterion
If the agent is chasing a flaky test, stabilize the test first:
# Run the same test 5 times — is it stable?
for i in {1..5}; do npm test -- --testNamePattern="login flow"; done
If you see 2+ failures, mock time / network / randomness before resuming:
// vitest setup
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-01-01'));
Once the test is stable, let the agent resume.
Step 4: Open a fresh session with clean context
If none of the above works, the last lever is to reset context:
- Copy the full current file contents (not the diff).
- Open a new session.
- Restart with this template:
Goal: [one-sentence goal]
Current code (already edited N rounds):
[paste full file]
Current failing test / error:
[paste full error]
Constraints: don't touch tests, don't touch config, only edit src/X.ts.
Output your plan first. Wait for my approval before editing.
A fresh context isn’t polluted by old failures and usually succeeds on the first try.
Step 5: Set a hard iteration cap
Prevent the next loop from happening. In Claude Code, add to CLAUDE.md:
## Agent behavior constraints
- Maximum 5 build/test iterations per task
- If still failing after 3 iterations, stop and report status for human decision
- Do not edit the same block of code in the same file more than 3 times
Cursor uses .cursorrules, Aider uses .aiderrules, Codex uses AGENTS.md — same idea.
Prevention
- Set iteration caps in
CLAUDE.md/AGENTS.md/.cursorrules— e.g., “stop and ask after 3 consecutive failures of the same test” - Inspect the agent’s reasoning / action trace, not just final output — loop patterns only show in the trace
- Isolate flaky tests into a
flaky.test.tsfile so agents never iterate against unstable feedback - On long tasks, force the agent to re-print “current plan + current progress” every 5 iterations
- Give the agent an explicit completion criterion — not “fix login” but “
npm test -- authall green” - When a loop appears, open a fresh session instead of trying to recover the polluted one
Related
- AI hallucinated a file that doesn’t exist
- Cursor missed project context
- AI pre-commit review workflow
- Claude Code SEO audit
- AI dependency upgrade workflow
- AI Generated TypeScript Errors
- AI Agent Overwrote .env / Environment Variables
- AI Suggested a Stale Dependency
- How to Help Claude Code Understand Your Project: 7 Methods That Actually Work
- AI Added a Route That Bypasses Auth Middleware
- AI Invented a Wrong API Signature That Does Not Exist
- AI-Generated SQL Locks a Hot Table for Minutes
- AI Keeps Using Deprecated Syntax Despite Lint Errors
- AI Uses npm Commands in a pnpm or Yarn Project
- AI Generated Migration Works on Dev, Fails on Prod Schema
Tags: #AI coding #Debug #Troubleshooting