Skip to content

Context Rot: Why Longer AI Agent Sessions Get Worse

CoreConceptJuly 29, 202610 min read

repo-agent is 34 turns into a routine rename: swap calculate_tax for compute_tax everywhere, except inside legacy/. Turn 1 stated that exception clearly. By turn 34, the agent renames the function inside legacy/billing_v1.py anyway, and a pinned compatibility test breaks. Nobody edited the constraint. Nobody fed it a wrong document. The window simply filled up with 33 turns of diffs, test logs, and revised plans, and the one line that mattered stopped competing.

That decay has a name now: context rot — the measurable drop in an LLM's reliability as the input it is reasoning over grows, independent of whether every fact in that input is technically correct. It is a different failure from bad retrieval or a sloppy prompt: the right information can be sitting right there in the transcript and still lose. This guide covers the mechanisms behind the drop, traces one session where it bites, and gives you a way to detect and bound it before it ships a silent bug. For the discipline of packing a single call well in the first place, see Context Engineering; this article is about what happens after that call, turn after turn.

Context rot map: four mechanisms that make a longer session less reliable, not more informed
Context rot map: four mechanisms that make a longer session less reliable, not more informed

What context rot actually is

Context rot is not "the model got confused" as a vibe — it is a specific, tested claim: as input length grows, model accuracy degrades on tasks it can solve easily at short lengths, even when the added tokens are relevant and coherent. Chroma's 2025 research tested 18 frontier models on controlled long-context tasks and found every one of them got measurably worse as input length increased — including simple retrieval and text-replication tasks that have nothing to do with reasoning difficulty. A model advertised with a 1M-token window does not reliably use all 1M tokens; some models showed serious accuracy loss well before their stated limit.

This matters for agents specifically because an agent session is not one call — it is dozens or hundreds of turns, each appending tool output, plans, and partial results to a transcript that the next call re-reads in full (or in a lightly trimmed form). A single well-packed prompt can still rot by turn 40, because the window is no longer one curated payload — it is an accumulated log nobody re-curated. Context rot is what context engineering becomes when you stretch it across a whole session instead of one call.

Quick reference

  • Definition: accuracy degrades as input length grows, even on easy tasks and even with correct information present.
  • Not the same bug as bad retrieval — the fact can be in the window and still lose the attention competition.
  • Not unique to one vendor or model family — reported across GPT, Claude, Gemini, and open-weight models tested.
  • Distinct from context engineering: engineering is per-call packing discipline; rot is what happens to that packing over many turns.
  • A stated context-window size is a ceiling, not a reliability guarantee at that length.

Remember this

Context rot means length itself is a reliability variable — a correct fact sitting in a long window is not the same as a fact the model actually uses.

Why longer sessions get less reliable

Four mechanisms compound across a session. Attention dilution: every additional token competes for the same fixed attention budget inside the model's layers, so any single fact's effective "share" of attention shrinks as more tokens surround it — this happens even if nothing you add is wrong. Lost-in-the-middle / position bias: facts near the very start or very end of the window are recalled more reliably than facts buried in the middle, so a constraint stated at turn 1 and never touched again drifts toward the least-reliable position as new turns get appended after it.

Distractor buildup is the one people underestimate: near-duplicate tool outputs (three similar diffs, five similar test-failure logs) interfere with retrieval more than an equal amount of random noise would, because the model has to discriminate between plausible-looking near-misses. Chroma's research adds a counterintuitive fourth finding: well-structured, coherent input can cost accuracy at scale more than shuffled input does — a clean, readable transcript is not automatically a safe one. None of these four require a wrong fact anywhere in the window; they are properties of length and structure, not correctness.

Why longer context degrades output: four measured mechanisms, not one single cause
Why longer context degrades output: four measured mechanisms, not one single cause

Quick reference

  • Attention dilution: fixed budget, more competitors — a structural property of the architecture, not a bug in one model.
  • Lost-in-the-middle: start and end recall best; the middle of a long transcript is the riskiest real estate.
  • Distractor buildup: near-duplicate tool logs interfere more than unrelated noise of the same length.
  • Structure cost: a tidy, well-organized long transcript is not exempt — coherent input can still degrade accuracy.
  • None of the four require an incorrect fact — this is why "just double-check the data" does not fix it.

Remember this

Rot comes from four measurable properties of length and structure, not from one bad fact — so fact-checking the transcript will not fix it.

One session, traced: RENAME-118

Running example. Subject: repo-agent, an autonomous coding agent. Input at turn 1: task RENAME-118 — "Rename calculate_tax to compute_tax everywhere; do not touch anything under legacy/." Success: every non-legacy call site renamed, full test suite green, legacy/ untouched. Failure: at turn 34, the agent renames the function inside legacy/billing_v1.py, and the pinned legacy-compatibility test fails.

Turns 2–20 accumulate real, useful work: diffs across a dozen files, test-runner output, a short replanning note after one file had two call sites instead of one. Turns 21–33 add a second replanning pass after the agent discovers a circular import, plus more test logs. None of these turns is wrong on its own — each is a faithful record of real progress. But the turn-1 constraint was never re-stated, never pinned, and by turn 34 it is deep in the middle of a transcript dominated by recent, structurally similar diff-and-log pairs. The model does not "decide" to violate the constraint; it simply never surfaces in the set of tokens that most strongly shape the next action.

repo-agent's RENAME-118 session: a turn-1 constraint survives 30 turns of tool noise, then gets buried
repo-agent's RENAME-118 session: a turn-1 constraint survives 30 turns of tool noise, then gets buried

Quick reference

  • Constraint token count: a handful of words — trivially cheap to keep, if you keep it deliberately.
  • Every intermediate turn is individually reasonable; the failure is a session-level property, not a single bad step.
  • The transcript in this example is fully coherent and well-formatted — the structure-cost finding applies here too.
  • A shorter session (turns 1–10) would very likely have respected the constraint fine.
  • This is the same failure mode whether the agent is renaming code, editing a spreadsheet, or triaging tickets.

Remember this

The constraint did not get overwritten or contradicted — it just aged out of the window's most-attended region while every individual turn looked correct.

Detecting it before it ships a bug

You cannot eyeball a 40-turn transcript and know whether a constraint survived — you have to test it. The pattern from long-context evaluation research (needle-in-a-haystack style checks) applies directly to agent sessions: periodically ask "is fact X still recoverable from this window?" and log the answer, rather than assuming length alone is safe because the model's advertised window is bigger than your transcript.

The two snippets below simulate the same shape of failure as RENAME-118 with plain string budgets, so you can reproduce and measure it without an API call. naiveWindow keeps only the most recent turns that fit a character budget — no compaction, no pinning. pinnedWindow reserves budget for a fixed set of must-survive facts and fills the rest with recent turns. Run both against the same 40-turn history and compare.

Zoom on turn 34: repo-agent renames calculate_tax inside legacy/billing_v1.py, violating the turn-1 constraint
Zoom on turn 34: repo-agent renames calculate_tax inside legacy/billing_v1.py, violating the turn-1 constraint

Quick reference

  • Instrument real sessions with a periodic self-check: ask the agent to restate active constraints, and diff against the original.
  • Log token/char count per turn — a session that keeps growing without compaction is a rot risk by construction.
  • Build a small golden set of "turn-1 fact → must still be true at turn N" checks, similar to a retrieval eval set.
  • Watch for the structure-cost finding: a clean, well-formatted transcript is not proof the model is still tracking it.
  • Treat rot as a session-level metric, not a per-call one — a single well-packed call can still rot by turn 40.
naiveWindow — the RENAME-118 failure, reproduced
1type Turn = { id: number; text: string };2 3function naiveWindow(turns: Turn[], maxChars: number): string {4  // Most-recent-first, no compaction, no pinning5  let out = "";6  for (let i = turns.length - 1; i >= 0; i--) {7    if (out.length + turns[i].text.length > maxChars) break;8    out = turns[i].text + "\n" + out;9  }10  return out;11}12 13const constraint = "CONSTRAINT: never rename inside legacy/";14const turns: Turn[] = [{ id: 1, text: constraint }];15for (let i = 2; i <= 40; i++) {16  turns.push({ id: i, text: `Turn ${i}: diff + test log ${"x".repeat(120)}` });17}18 19const naive = naiveWindow(turns, 4000);20console.log("naive keeps constraint:", naive.includes("legacy/"));21// Expected: false by turn ~30 — the turn-1 line has scrolled out of the budget
pinnedWindow — pin the fact, don't hope it survives
1function pinnedWindow(turns: Turn[], pinned: string[], maxChars: number): string {2  const pinnedBlock = pinned.join("\n");3  const budget = maxChars - pinnedBlock.length;4  let recent = "";5  for (let i = turns.length - 1; i >= 0; i--) {6    if (recent.length + turns[i].text.length > budget) break;7    recent = turns[i].text + "\n" + recent;8  }9  return pinnedBlock + "\n" + recent;10}11 12const pinned = pinnedWindow(turns, [constraint], 4000);13console.log("pinned keeps constraint:", pinned.includes("legacy/"));14// Expected: true — pinning holds the fact at a fixed, high-attention position15// regardless of how many turns get appended after it

Remember this

Naive most-recent-first windowing loses old constraints on a schedule you can calculate — measure it with a fixed budget, don't assume the model will remember.

Bounding it: what actually works

Pick the fix that matches what actually failed, the same way you'd pick between prompt, context, and harness fixes in Prompt vs Context vs Harness. If constraints and decisions are getting buried, compaction — periodically summarizing the transcript into a short decision log and re-pinning must-keep facts — is the cheapest fix and the one pinnedWindow demonstrates above. If a subtask is long and mostly self-contained (a refactor pass, a research dive), sub-agent isolation gives it a fresh, short window and returns only the result to the parent, which never accumulates that subtask's noise at all.

When a fact needs to outlive the session entirely — not just one long conversation — move it out of the transcript into a persistent scratchpad or memory store that gets re-read every turn instead of re-derived from history; see AI Agent Memory for the storage patterns. And when the risk of a silent violation is high enough that you want a human in the loop before it compounds, cap the turn budget and checkpoint: stop the agent, summarize, and get a review before it resumes — the same checkpoint discipline covered in Human-in-the-Loop AI Agent Patterns. None of these are mutually exclusive; production agent harnesses typically run compaction on a schedule and still cap total turns as a backstop.

Four mitigations for context rot, matched to what actually failed
Four mitigations for context rot, matched to what actually failed

Quick reference

  • Compaction: cheapest, needs a summarizer you trust not to drop the pinned facts itself.
  • Sub-agent isolation: best for bounded subtasks; adds orchestration and result-passing complexity.
  • Persistent scratchpad: best for facts that must survive across sessions, not just within one.
  • Turn budget + checkpoint: the backstop when you cannot fully trust the other three yet.
  • Never treat a bigger context window as the fix — Chroma's tests show bigger windows still rot, just at a longer turn count.

Remember this

Match the fix to the failure: compact to stop burial, isolate to stop noise, persist to survive across sessions, and checkpoint to bound the blast radius when you cannot fully trust the other three yet.

Key takeaway

Context rot reframes a familiar agent bug — "it forgot the rule I gave it" — as a measurable, length-driven property rather than a one-off mistake. The fix is not a longer context window or a better-worded instruction; it is treating the session itself as something that needs active curation, the same way you already curate a single call.

Practice (20 min). Paste both naiveWindow and pinnedWindow from the detection section into context-rot-check.ts and run npx tsx context-rot-check.ts. Expected result: naive keeps constraint: false and pinned keeps constraint: true. Now break it on purpose — drop maxChars to 200 in the pinned call. Expected: the pinned block itself no longer fits and the function throws or returns an empty recent window. Recover by raising maxChars back above the pinned block's length, or by shortening the pinned constraint text. Pass criterion: pinnedWindow keeps the constraint at every turn count you test (10, 40, 200), while naiveWindow loses it by a turn count you can calculate from maxChars divided by average turn length.

Share:

Related Articles

Jul 29, 2026 · 3 min read

AI Workflow State Machines matters when a team has to turn an AI idea into a system other people can trust. The useful q

Read

Jul 29, 2026 · 3 min read

AI Task Decomposition for Agents matters when a team has to turn an AI idea into a system other people can trust. The us

Read

Jul 29, 2026 · 3 min read

AI Model Routing Strategies matters when a team has to turn an AI idea into a system other people can trust. The useful

Read

Explore this topic

Keep learning

Follow a structured path or browse all courses to go deeper.