The retry loop: spotting it and breaking it
Two attempts on the same error is the whole budget, and here is exactly what to do when the second one fails.
"Let me try a different approach." You have now read that sentence four times in eleven minutes. Each time it was followed by a diff differing from the previous one by roughly six characters, and each time the same assertion failed with the same message. The narration stays confident. The code is not converging.
This is the most expensive failure mode in agent-assisted development, and it is expensive precisely because every individual step is cheap. No single retry ever feels like the mistake.
The mechanics
The loop has a shape. Attempt, fail, near-identical attempt, fail, slight variation, fail. What makes it self-sustaining is what happens to the context.
Every attempt appends its own output to the session: the diff, the error, the reasoning, the tool calls. After six rounds the majority of what the agent is reasoning over is its own failed work. Your original problem statement is a small and shrinking fraction of the input, while the wrong approach is now represented six times over. It is not stuck for lack of ability, but because the evidence in front of it is dominated by variations on the thing that does not work.
That is why attempt seven is worse than attempt two, and why "try again" gets less effective the more you lean on it.
Why it happens
No new information enters between attempts. This is the core of it. Attempt two has access to exactly what attempt one had, minus some clarity. Without new evidence you are resampling from roughly the same distribution β rolling the same die and expecting a different number.
The cause sits outside what the agent can see. The wrong Node version. A stale dist/ imported instead of src/. A dev server that was never restarted, so none of the last four edits are actually running. The code is fine, the agent cannot see any of that, so it keeps editing the one thing it can see.
The error message is being misread. Module resolution failures, type errors that point at the call site rather than the definition, and anything wrapped by a framework's error handling all reliably misdirect. The agent fixes what the message appears to say.
The objective quietly changed. Somewhere around attempt three, "make this correct" becomes "make this error message stop". Only one of those is your goal, and the other is always achievable, because a try/catch will do it.
Hard signals
You want signals you can check without judgement, because by round four your judgement is degraded too.
- The same error text appears twice. Not a similar error. The identical string.
- The diff keeps growing while the explanation gets vaguer.
- "Let me try a different approach" for the third time.
- A
try/catchappears around the failing call. - An assertion is weakened, loosened or deleted.
--force,--legacy-peer-depsor--no-verifyshows up in a command.any,as unknown as,@ts-ignore,# type: ignoreor// eslint-disableappears in the diff.
The last three matter most, because they mean the loop has already broken in the wrong direction. The error is gone. The bug is still there, now wearing camouflage. Any of these firing means stop, not "review this one carefully".
Breaking it, in order
Stop generating. Nothing else works while output continues. Interrupt mid-turn if you have to.
Revert to a clean tree first. This is the step people skip, and skipping it is why the next hour also goes badly. After four attempts your working tree holds four partial fixes layered on each other, at least two of which are wrong and one of which may be masking the original symptom. You are no longer debugging your bug.
git diff --stat # see the damage first
git stash push -u -m "retry attempts, probably bin"
npm test # confirm it still fails from clean
Stash rather than git checkout . if anything in there might be worth keeping. If it was all noise, git checkout . and move on. Attempt five should start from the state attempt one did.
Add information, not attempts. Run one diagnostic yourself and paste the raw output back. Not your interpretation of it. The actual bytes.
node --version && cat .nvmrc
npm ls react # two copies of a dep hiding in the tree
rg -n "from ['\"]\.\./dist" src/ # importing build output instead of source
ls -la .env*
One command with real output is worth ten more attempts, because it is the only item on this list that changes what the model knows.
Forbid edits and ask for hypotheses. Explicitly: no file changes, three hypotheses, and for each one the single observation that would confirm or eliminate it. This works because it changes what a good response looks like. The agent is no longer being scored on producing a diff.
Start a fresh session. If two or three rounds of that still get you nowhere, the context itself is the problem. Open a new session, state the problem cleanly, include the failing command, the verbatim error and the diagnostics you gathered, and deliberately leave out every failed attempt. You are not throwing away progress. You are removing six worked examples of what does not work from the evidence pile. If the session was compacted somewhere back there, the summary kept the shape of those failures and lost the detail that mattered, which makes a fresh start more urgent, not less.
The boring-cause checklist
Rule these out before hypothesis three. They are invisible to an agent reading source files, which is exactly why they survive six attempts.
- Is the server or watcher actually restarted and running the file you edited?
- Is the runtime version what the project expects β
node --versionagainst.nvmrcorengines? - Is there a stale build artefact:
dist/,.next/,__pycache__,tsconfig.tsbuildinfo? - Are dependencies installed at the versions in the lockfile?
- Is there a second copy of the dependency somewhere in the tree?
- Are you running the test you think you are, in the file you think it lives in?
- Is the environment variable set in the shell running the process, rather than the one you typed it into?
- Did an earlier attempt leave something behind that is now causing the failure?
Number eight is why the clean tree comes first.
What the loop costs
Retries are not free in any of three currencies. They cost money in tokens, which is the one people notice. They cost context, which is the one that actually hurts, because space consumed by failed attempts is space unavailable for the code that would solve the problem. And they cost attention, which does not recover inside the same session.
A long loop also trains you to stop reading diffs. By attempt six you are skimming, checking only whether the test went green, and that habit outlives the bug. If the real issue is that the change needs judgement you have not supplied, no number of retries reaches it β a different decision, covered in When to stop the agent and write it yourself.
The two-strike rule
Two attempts on the same error. That is the entire budget.
When the second one fails, do not type "try again" and do not type "think harder". Revert to a clean tree, run one diagnostic yourself, then paste this:
Stop. Do not edit any files.
Failing command: npm test -- src/webhooks/checkout.test.ts
Verbatim error: <paste the full error including the stack>
Diagnostics I ran: <paste the raw output>
Give me three hypotheses for the cause, ordered by likelihood.
For each: the single observation that would confirm or eliminate it,
and the exact command I should run to get that observation.
Consider causes outside the source files - versions, stale builds,
cached dependencies, processes that were never restarted.
Then run the first command it gives you. You are back to gathering evidence, which is the only activity that has ever ended one of these. If you are doing this inside a codebase you did not write, Debugging an AI-generated codebase you have never read picks up from there.
Keep reading
Shipping to production what an agent built
Agent code is correct on the happy path and thin everywhere else, so shipping means checking the parts nobody prompted for.
Structuring a repo so an agent can navigate it
Six concrete changes that stop your agent burning nine tool calls working out where the routes live.
When to stop the agent and write it yourself
Six signals that another prompt is wasted, and the skeleton-and-fill pattern that beats both retrying and taking over completely.