Here’s the failure mode that surprises people who’ve only reasoned about agents statistically. You measure a per-step error rate — say 10% of steps produce something wrong — and you assume errors are independent, so a wrong step is a wrong step and the rest of the run is fine. Then you watch a real trajectory and see something else: step 4 gets a fact slightly wrong, step 5 reasons on top of that wrong fact and commits harder, step 6 takes an action premised on both, and by step 8 the agent is confidently executing a plan that was doomed at step 4. One mistake became five. The errors weren’t independent — they were coupled through the context, and coupling is what turns a 10% step-error rate into a run that’s wrong far more than 10% of the time.

This is the cascade: a single fault amplifying down a single trajectory. It’s distinct from the failure I wrote about in distributed retry patterns, where the problem is one bad condition hitting many workers at once — that’s a blast radius, a horizontal spread. The cascade is vertical: it spreads through time within one run, because an agent’s own past output is its future input. This post is about the vertical kind, why it’s structural rather than bad luck, and where you can cut it.

Why coupling is the default, not the exception

A stateless function that fails just returns an error. An agent that fails does something worse: it writes the failure down where it can read it again. The mechanism is the same one that makes agents work at all — the transcript accumulates, and every step conditions on everything before it. That’s a feature for carrying intent forward. It’s also the exact channel a mistake travels down.

Three ways a single fault propagates through the context:

  • Poisoned premise. The agent derives or retrieves a wrong fact — a misparsed tool result, a hallucinated ID, a stale value — and it lands in the transcript as if it were true. Every subsequent step treats it as established. The model doesn’t re-litigate settled facts; that’s usually good, and here it’s how a small error becomes load-bearing.
  • Error residue. A step fails, and the error observation stays in the window so the model can recover from it. But the failed attempt is also still there, and sometimes the model anchors on its own bad first draft instead of the correction — or the error text itself misleads the next step’s reasoning. (Tool output is untrusted input for exactly this reason: the residue in your context isn’t all trustworthy.)
  • Committed action. The worst one. The agent doesn’t just believe something wrong, it does something wrong — writes a bad record, sends a message, mutates state — and now later steps have to cope with a world that’s actually been changed. You can drop a wrong fact from context. You can’t un-send an email.

In all three, the common structure is: the fault becomes part of the state the agent reasons from, so it’s no longer one wrong step — it’s a wrong starting condition for every step that follows.

A model of the amplification

Let’s put a number on it. Model a run as N steps. Each step, absent any prior damage, fails on its own with probability p. But once a run is “contaminated” — a fault has entered the context — every subsequent step fails with an elevated probability p_c > p, because it’s reasoning on a poisoned premise. That’s the coupling, expressed as one conditional.

import random, statistics

N   = 8       # steps per run
p   = 0.10    # baseline per-step fault probability
p_c = 0.45    # per-step fault probability ONCE the run is contaminated

def run_cascade(trials=200_000):
    total_faults, contaminated_runs = 0, 0
    for _ in range(trials):
        contaminated, faults = False, 0
        for _step in range(N):
            fail_prob = p_c if contaminated else p
            if random.random() < fail_prob:
                faults += 1
                contaminated = True          # the fault poisons the rest of the run
        total_faults += faults
        contaminated_runs += 1 if contaminated else 0
    return total_faults / trials, contaminated_runs / trials

def run_independent(trials=200_000):
    total = sum(sum(random.random() < p for _ in range(N)) for _ in range(trials))
    return total / trials

faults_coupled, contam = run_cascade()
faults_indep = run_independent()
print(f"independent model: {faults_indep:.2f} faults/run")
print(f"coupled model:     {faults_coupled:.2f} faults/run  ({contam*100:.0f}% of runs contaminated)")
print(f"amplification:     x{faults_coupled/faults_indep:.2f}")

Running it:

independent model: 0.80 faults/run
coupled model:     1.61 faults/run  (57% of runs contaminated)
amplification:     x2.01

Same 10% baseline step-error rate. Under the independent assumption you expect 0.8 faults per run and move on. Under coupling you get twice as many, and more than half your runs end up contaminated — carrying at least one fault that then bred more. The baseline p didn’t change. What changed is that the model stopped pretending a mistake sits still.

And the cascade gets worse with run length, which is the tell that distinguishes it from independent noise:

N=4    x1.5 amplification
N=8    x2.0
N=16   x2.7
N=25   x3.2

Independent faults scale linearly with N — twice the steps, twice the expected faults, same rate. Cascading faults scale super-linearly, because a longer run gives an early fault more downstream steps to poison. This is the same shape as the O(N²) token curve: long agent runs are where the structural problems live, cost and correctness alike.

Where to cut the cascade

You can’t drive p to zero. The leverage isn’t in never making the first mistake — it’s in stopping the first mistake from becoming the next five. Three interruption points, roughly in order of leverage:

  1. Lower p_c, not just p. The contaminated-state failure probability is the whole ballgame. That means giving the agent a way to notice and discard a poisoned premise — verification steps that re-derive a fact from source rather than trusting the transcript, or a checkpoint that re-grounds the agent in the actual current state instead of its narrative of it. Halving p_c from 0.45 to 0.22 drops the coupled model from 1.61 faults/run to about 1.08 — most of the way back to the 0.80 independent floor. That’s a bigger win than any realistic cut to p.
  2. Quarantine committed actions behind a confirmation boundary. The believe-wrong faults are recoverable; the do-wrong ones aren’t. Put the irreversible actions — writes, sends, state mutations — behind a gate that a poisoned premise has to survive: a validation check, a dry-run, a human confirm on the high-stakes ones. This doesn’t stop contamination; it stops contamination from escaping the run into the world, which is the difference between a wasted run and an incident.
  3. Detect contamination and abort. A run that’s failed twice is very likely contaminated (57% of runs in the model carry a fault; among those, more are coming). Rather than let it grind out N steps of increasingly-wrong work — burning tokens and tool fees the whole way, since every axis of cost rides along — trip a per-run fault counter and abort early into a clean restart or an escalation. An early abort caps both the damage and the bill.

Notice these are different tools than the fleet post prescribed. Circuit breakers and shared budgets bound the horizontal spread across workers; they do nothing for the vertical spread inside one run. A single worker with a clean circuit breaker can still cascade itself into a completely wrong answer. You need both: blast-radius controls for the fleet, cascade controls for the trajectory.

What I’d actually do

  • Stop assuming independence. If your reliability math treats step errors as independent, it’s under-counting your real error rate by roughly the amplification factor — 2–3× at these rates, and climbing with run length. Measure faults-per-run, not just fault-rate-per-step, and watch whether it scales super-linearly with length. If it does, you have a cascade, not noise.
  • Instrument the first fault in a contaminated run, not just the final failure. The last step is where the run visibly breaks; the first fault is where it was actually lost. Fixing the visible break treats a symptom several steps downstream of the cause.
  • Spend your reliability budget on p_c. Verification, re-grounding, and early-abort attack the coupling directly. Chasing p polishes individual steps while leaving the amplification untouched — and the amplification is most of the problem.

One bad step is not one bad step. It’s a starting condition, and the agent will faithfully build on it until something makes it stop. Your job isn’t to prevent the first mistake — it’s to make sure the second one doesn’t inherit it.

The cascade model here is a toy Monte Carlo with a single contamination state; real trajectories have partial recovery and varying p_c by step, which you can add. The structural claims — coupling through context, super-linear scaling with length, p_c as the dominant lever — transfer across providers and models.