Why AI Agents Fail: Context Rot, Loops and Error Handling

Agent failures are rarely dramatic. Nothing crashes, no exception surfaces, and the logs look ordinary. The run simply drifts away from the task, repeats a broken step forty times, or announces completion of work it did not do. All six of the common failure modes are diagnosable and most are preventable — but only if you instrumented for them before they happened.

The short version

Agents fail through accumulation, not through single bad decisions. Every step adds context, and long contexts contain old failures, stale plans and buried instructions that all keep influencing the next choice.

The two controls that matter most: cap everything in code, and log full trajectories. Without the second, none of the rest is diagnosable.

1. Context rot

An agent's context grows monotonically. Step twenty is reasoned over a window containing nineteen previous decisions, their results, and every dead end along the way.

// Step 3 — clean, focused [system] [task] [1 result] // Step 25 — the task is a rounding error in the window [system] [task] [result] [failed attempt] [retry] [result] [wrong path] [correction] [result] [result] [stale plan] [error] [retry] [result] ... × 20 more // The original instruction is still there. It is // now 2% of what the model is attending to.

Three distinct things degrade at once, and it is worth separating them because the fixes differ:

  • Instruction dilution. The task statement is proportionally tiny. Restate the objective periodically — every few steps, injected fresh — so it is never far from the current position.
  • Failed attempts still voting. An approach that did not work remains in context, and the model can revisit it as though it were untried. Prune dead branches: summarise a failure into one line and drop the detail.
  • Stale facts. A file read at step four may have been edited at step eleven. The old contents are still sitting there, and nothing marks them superseded.

💡 Compact rather than truncate

When context grows too long, the naive fix is dropping the oldest messages. That removes the task statement and early decisions — usually the most important content in the window.

Compaction preserves the shape instead: keep the system prompt and objective verbatim, replace the middle with a summary of what was learned and tried, keep the last few steps in full. The window shrinks and the thread survives.

2. Loops

The most visible failure and the easiest to prevent. Its cause is worth understanding precisely: a failure does not change the reasoning that produced the action.

step 8 read_file{"src/utils.js"} → ENOENT step 9 read_file{"src/utils.js"} → ENOENT step 10 read_file{"src/utils.js"} → ENOENT // The model still believes that file is where the answer is. // Nothing in "ENOENT" tells it what to do instead.

Two fixes, and you want both. In your code, detect repetition — the same tool with the same arguments twice in a row is a signal, three times is a certainty — and intervene rather than executing again:

if (isRepeat(call, history, 2)) { return "This exact call has already failed twice. Do not repeat it. Either try a different approach or state that you cannot proceed and why."; }

And in your tool results, make errors actionable. "ENOENT" is a dead end; "no such file — src/ contains helpers.js, index.js, parse.js" gives the model somewhere to go. Most loops are error messages that failed to suggest an alternative.

3. False completion

The agent reports success. Nothing was done, or half was done.

This happens because finishing is a judgement the model makes about its own work, and self-assessment is exactly the thing language models are least reliable at. An agent that has generated a plausible summary of completed work has, from the inside, no way to distinguish that from having completed it.

🚨 Never let the model be the judge of done

If your only completion signal is the agent saying it finished, you have no completion signal. Verify in code, against the actual world:

// Not this if (response.includes('done')) markComplete(); // This — check the thing that was supposed to change const ok = await verify({ filesExist: expectedOutputs.every(fs.existsSync), testsPass: await runTests(), rowsWritten: await countRows() === expected }); if (!ok) return continueWith("Verification failed: ...");

Verification is also the highest-value thing to feed back. An agent told specifically which check failed will usually fix it; an agent told "that was wrong" will guess.

4. Goal drift

The agent was asked to fix a failing test. Twelve steps later it is refactoring the module, and the test is still failing.

Drift is context rot's behavioural symptom, and it usually enters through a plausible intermediate step. Fixing the test seemed to require understanding the module; understanding it surfaced something untidy; tidying it became the work. Each individual step is defensible and the destination is wrong.

  • Restate the objective every few steps as a fresh message, not a buried one.
  • State the scope boundary explicitly — "do not modify files outside the ones you were given" is a constraint the model can actually apply.
  • Check relevance periodically. A cheap side call asking whether the last three actions advanced the stated goal catches drift early and costs very little.

5. Tool confusion

Wrong tool, right intent. This is a design failure in the tool surface far more often than a model failure.

SymptomCauseFix
Picks the wrong one of two similar toolsOverlapping descriptionsSay what each is not for
Ignores a tool entirelyDescription does not match phrasingName the triggering situation
Invents parametersValue absent from contextMark required; validate
Degrades as tools are addedToo many toolsCut, or route to a subset

The last row is the one that surprises people. Tool selection accuracy falls as the menu grows, and it falls faster than linearly — twenty overlapping tools are much worse than ten distinct ones. If an agent is picking badly, removing tools is more often the fix than improving the prompt. The groundwork is in how you design the schemas.

6. Cost blowout

Not strictly a failure of the output, but the one that ends projects. Agent cost is the product of steps, context length and model price, and the first two both grow during a run.

// Cost per step rises as context accumulates step 1: 2,000 tokens in step 10: 28,000 tokens in step 30: 140,000 tokens in // each step re-sends everything // A 30-step run is not 30x a 1-step run. // It is closer to quadratic in the number of steps.

This is why an agent that occasionally takes sixty steps instead of six is not ten times the cost — it can be a hundred. Cap iterations, cap cumulative tokens, cap wall-clock time, and enforce all three in code. Prompt-level instructions to be efficient are not controls.

What to log

Agent failures are properties of a sequence. A log recording only the final input and output cannot explain any of the six modes above.

// Per step, tied to a run ID { "run_id": "a3f9", "step": 14, "context_tokens": 48210, "tool": "read_file", "args": { "path": "src/utils.js" }, "result_status": "error", "result_preview": "ENOENT", "duration_ms": 42, "cumulative_cost": 0.38 }

With this you can answer the questions that matter: where did context start growing faster than the work, which step first went off task, how many distinct actions were attempted versus repeated, and what the run had cost by the time it went wrong. Without it, "the agent did something strange yesterday" is permanently unanswerable.

⚠️ Shorter runs are more reliable runs

Every failure here worsens with trajectory length. The instinct is to make the agent better at long runs; the more effective move is usually to need fewer steps.

Split a fifty-step task into five ten-step runs with explicit handoffs. Each starts with clean context, each has a verifiable output, and a failure costs one segment rather than the whole thing. Much of what looks like an agent reliability problem is a task decomposition problem — and often the honest conclusion is that a workflow should have run those segments in the first place.

Inspecting agent logs and traces?

Format, filter and explore JSON logs entirely in your browser — nothing is uploaded to a server.

Open JSON Formatter →

Summary

  • Agents fail by accumulation, not by single bad decisions.
  • Context rot dilutes instructions, keeps failed attempts in play and preserves stale facts.
  • Compact, do not truncate — keep the objective, summarise the middle.
  • Loops persist because failure does not change the reasoning. Detect repeats in code; make errors actionable.
  • Never let the model judge completion. Verify against the world.
  • Drift enters through plausible intermediate steps. Restate scope and objective.
  • Cost grows closer to quadratically with steps. Cap iterations, tokens and time.
  • Log full trajectories, or agent bugs are not reproducible.

Frequently Asked Questions

What is context rot in AI agents?

The degradation in an agent's performance as its conversation grows longer. Early instructions get buried under accumulated tool output, failed attempts stay in view and continue influencing decisions, and the signal-to-noise ratio of the context falls with every step. The model is not getting worse — the material it is reasoning over is.

Why do AI agents get stuck in loops?

Because a failing action still looks like the correct action. The model chooses a step, it fails, and nothing in the failure changes the reasoning that produced the choice — so the same step gets chosen again. Without a loop detector in your code, this repeats until an iteration cap or a budget stops it.

Why does an agent say it finished when it did not?

Because completion is a judgement the model makes about its own work, usually without verifying it. If nothing independently checks the result, an agent that believes the task is done will report success. The fix is a verification step in your code rather than the model's own assessment.

How do I debug an AI agent?

By logging the full trajectory: every prompt, decision, tool call, argument set, result and token count, tied to a run ID. Agent failures are properties of a sequence, not a single call, so a log that records only inputs and outputs cannot explain them. Without trajectory logs most agent bugs are not reproducible.

How long should an agent be allowed to run?

Shorter than feels natural. Reliability falls as trajectories lengthen, so a task needing fifty steps is usually better split into several shorter runs with fresh context and a handoff between them. Hard caps on iterations, tokens and wall-clock time should be enforced in code regardless.

P

Written by Paras

We build free, browser-based file tools and write the reference material we wish existed when we were looking things up. Spotted an error? Tell us and we will fix it.