Networks blip. Providers rate-limit you. The context window overflows mid-thought. A toy agent falls over; a real one treats failure as normal and recovers on its own. This lesson builds the four self-healing reflexes — retry, failover, compaction, and diagnostics — that keep a gateway standing.
By Lesson 8 your gateway can hold a conversation, run an agent, call a model, use tools, and remember. Every one of those steps assumed the happy path: the network was up, the model answered, the context fit. On your laptop, for five minutes, that assumption holds. Run the same gateway for a week and it stops holding the way "the sun is up" stops holding — reliably, on a schedule you didn't pick.
Here is the uncomfortable arithmetic. Suppose any single model call succeeds 99.9% of the time — one failure in a thousand, which sounds great. A busy agent that makes ten thousand calls a day will then see roughly ten failures every day. Not "if." When. At scale, rare events are not rare; they are a steady drizzle. The question is never "will something fail?" but "what does the system do when it does?"
A toy agent answers that question by crashing, or by handing the user a stack trace, or by silently producing garbage. None of those is acceptable for a thing you talk to from your phone all day. The fix is a change of stance: stop treating failure as an exception you didn't plan for, and start treating it as the normal case — an input the system is designed to absorb.
Those reflexes map cleanly onto the four ways a gateway tends to break. A transient hiccup — a dropped packet, a one-second outage — calls for retry: try again, a little later. A whole provider going down — your model vendor is overloaded — calls for failover: switch to a different model. A conversation growing past the model's memory — the context window overflows — calls for compaction: shrink what the model sees. And a run that seems frozen calls for diagnostics: figure out whether it is genuinely wedged or just doing slow honest work.
The rest of this lesson is those four reflexes, one or two chapters each, built up from the simplest version to the exact rules OpenClaw uses in production. By the end you'll have a map: this kind of failure, that reflex. Let's start with the most common failure of all — the transient blip — and the most misunderstood fix for it.
The first reflex is the simplest one to state and the easiest to get subtly wrong. When a call fails for a transient reason — a momentary network drop, a provider that was busy for half a second — the right move is often just to try again. The failure was a flicker; a second attempt sails through. But "try again" hides three decisions: how many times, how long to wait, and whether everyone should wait the same amount.
Start with how many times. Not forever — an infinite retry loop on a genuinely-down service is a way to hang your whole system. OpenClaw's default is 3 attempts: try, and if that fails, try twice more, then give up and let a different reflex (failover, next chapter) take over. Three is a sweet spot — enough to ride out a brief blip, few enough to fail fast when the blip is actually an outage.
Now how long to wait between attempts. Retrying instantly is a mistake: if the service is briefly overloaded, hammering it again immediately just adds to the load that caused the failure. So we wait — and we wait longer each time, because a failure that survived one short wait probably needs a longer one. This is exponential backoff: the delay doubles each attempt. One second, then two, then four, then eight. The idea is to back off the busy service gently, giving it room to recover.
Here is the decision people miss, and it is the whole reason this chapter exists. Imagine a provider hiccups and a thousand clients all get the same failure at the same instant. If every client uses the exact same backoff schedule — all wait one second, all retry — then a thousand retries land on the recovering service at the same moment, one second later. That synchronized wave is called a thundering herd, and it re-causes the very outage it was trying to recover from. The fix is jitter: add a small random wobble to each client's delay — OpenClaw uses about 10% — so the thousand retries spread out across a window instead of stacking on one instant.
The bars show the delay before each attempt: doubling early, then flattening at the 30 s cap (the dashed line). Turn on thundering herd to release many clients at once: without jitter their retries pile onto the same instants (tall red spikes); with jitter they spread into a smooth band. Drag the jitter slider and watch the spikes melt.
So the full reflex is: try up to three times; wait an exponentially-growing delay between tries, capped at 30 seconds; and stir in 10% jitter so a crowd of clients doesn't retry in lockstep. Let's build the schedule itself — the function that turns an attempt number into a delay — and watch the curve double, flatten at the cap, and wobble.
Retry is powerful, but applied blindly it is also harmful. The previous chapter quietly assumed every failure was a transient blip worth a second try. Most aren't. Retrying the wrong failure wastes time at best and corrupts data at worst. So before the retry reflex fires, the gateway runs a quick triage: error classification — is this the kind of failure that a retry could fix, or not?
The dividing line is simple to state. Transient failures are the fault of the moment, not the request: the network reset, the provider was momentarily busy, a request timed out. Try the same request again and it may well succeed. Permanent failures are the fault of the request itself: you asked for something that doesn't exist, or sent a malformed body, or weren't authorized. Trying the identical bad request a second time will fail in the identical way — you've just wasted a round trip.
The status numbers are worth a moment, because they encode who is at fault. The 4xx range means "the client made a mistake" — the request was bad. The 5xx range means "the server made a mistake" — the request was fine but something broke on their end. That single distinction is most of the classifier: a server-side error (5xx) might clear up on its own, so retry; a client-side error (4xx) won't, so don't. The exceptions are the few 4xx codes that are actually transient — 408 (a timeout dressed as a client error), 409 (a conflict that may resolve), and 429 (rate-limited, which is explicitly "try again later").
That last one, 429, often comes with a gift: a Retry-After header, where the provider tells you exactly how long to wait before trying again. Honor it — it overrides your computed backoff, because the provider knows its own recovery time better than your formula does. But put a ceiling on your patience: if Retry-After says "wait more than 60 seconds," OpenClaw stops retrying and surfaces, so a different reflex can take over. A minute-long wait isn't a blip anymore; it's a sign this provider is down, and the right move is to fail over to another one (next chapter), not to sit and wait.
So the classifier is the gatekeeper standing in front of the retry reflex. Transient signal? Hand it to retry. Permanent client error? Surface it now. Rate-limited with a short Retry-After? Wait exactly that long. Rate-limited with a long one? Stop and let failover step in. Triage first, then react — which brings us to what happens when retry gives up entirely.
404 Not Found (a 4xx client error). Should the retry reflex try it again?Retry handles a flicker. But sometimes the thing flickering is the whole provider — your model vendor is overloaded, or your API key got rate-limited for the next hour, or billing got disabled. No number of retries against that provider will help; you need a different one. That is the second reflex: failover — when a whole provider is unusable, switch to another model.
For failover to work, the gateway needs a list of which models to try, in order. OpenClaw builds this candidate chain from three pieces, and the order is deliberate:
With the chain built, failover walks it. Try the first model. If it answers, you're done — return that model's result. If it fails, the question is whether to advance to the next candidate or stop walking entirely. That decision is the heart of failover, and it depends on why the model failed.
Advance when the failure is the provider's fault and another provider might do better: an auth failure (this provider rejected our credentials), a rate limit (this provider is throttling us), being overloaded or busy (this provider is swamped), a timeout (this provider didn't answer in time), or billing disabled (this provider's account is frozen). All of those say "this provider can't help right now — try the next one."
Let's build the walker exactly. Two pieces: assemble the candidate chain (with the dedup that keeps a requested-equals-primary model from appearing twice), then walk it — returning on success, advancing on a provider failure, and stopping cold on a context overflow or an abort.
Failover walks past a broken provider once. But it would be foolish to walk right back to that same broken provider on the next request, a second later, only to fail over again. A provider that just rate-limited you is going to rate-limit you again immediately. So when failover skips a provider, the gateway also puts that provider in a cooldown: a timeout during which we don't even try it. Stop hammering the thing that's down.
And the cooldown grows if the provider keeps failing — the same exponential idea as backoff, applied to whole providers. The first failure earns a short rest; repeated failures earn longer ones, because a provider that's failed three times in a row is probably going to keep failing for a while.
There's a second knob in the same spirit. A provider failure isn't always the provider's fault — sometimes it's the specific credential you're using. One API key got rate-limited; another might be fine. So alongside cooldowns, the gateway can rotate auth profiles: it holds several ways to authenticate and picks a different one when the current credential is failing.
The rotation order is deliberate. OpenClaw prefers OAuth profiles before raw API keys (OAuth tokens tend to carry higher limits and cleaner identity), and among equals it picks the oldest-used first — the credential that's had the longest rest, so usage spreads evenly instead of pounding one key.
So between failover (skip the broken provider now) and cooldown (don't come back too soon) and auth rotation (try a fresher credential before giving up on the provider entirely), the gateway has a graded response to provider trouble — from "wait a beat" all the way to "leave it alone for a day." Next we turn to a failure that no amount of switching providers can fix: the conversation that outgrows the model's memory.
The third reflex answers a failure unique to language-model agents. Every model can only read a fixed amount of text at once — its context window (Lesson 5). A long conversation keeps growing: every user message and every agent reply is appended to the transcript the model sees. Eventually the transcript brushes up against the window, and the next call returns a context-length-exceeded error. The model has, in effect, run out of memory mid-thought.
We saw in Chapter 3 that switching providers won't help — the next model has a window too. The real fix is to make the transcript smaller. That's compaction: when the transcript nears the window (or the model explicitly says it overflowed), summarize the older turns into one compact entry and keep the recent tail verbatim. The distant past becomes a paragraph of summary; the recent exchanges stay word-for-word, because that's what the current reply depends on.
So far so simple: cut the transcript at some boundary, summarize everything before it, keep everything after. But there is a trap that makes a naive cut corrupt the conversation, and it's the whole reason compaction needs care.
Recall how tool use works (Lesson 4). When the agent calls a tool, the transcript records two entries that belong together: the tool call ("I'm calling the weather tool") and, right after it, the tool result ("the weather tool returned: 72°F sunny"). The model's format requires these to come as a pair — a tool result with no preceding tool call is malformed, and the API will reject it outright.
Let's build compaction with exactly that safety. Choose a boundary so the last few entries are kept verbatim; but if that boundary falls between a tool call and its result, move it back to keep the pair whole. Summarize everything before the (possibly adjusted) boundary into one marker, and watch the orphan bug appear in the naive version and vanish in the careful one.
The first three reflexes handle failures that announce themselves: an error code, an overflow message, a refused connection. The fourth reflex handles the sneakier case — a run that just sits there. Nothing has errored. No exception was thrown. The agent is simply… not finishing. Is it wedged, or is it doing slow honest work?
This matters because the two cases demand opposite responses. A genuinely stuck run should be killed and restarted — leaving it pins resources and blocks the lane. But a run that's legitimately slow — a big tool call, a long generation — must be left alone; killing it throws away real progress and frustrates the user. Get this wrong and you either let zombie runs accumulate, or you murder good work. The gateway needs to tell them apart.
The distinction the classifier is really drawing is between absence of progress and slowness of progress. A long-running run shows movement — the token count climbs, the tool reports steps — so the gateway leaves it be. A stalled run shows a flat line: the same state, message after message, with no forward motion. Stuck sits in between: not moving, but for an identifiable external reason (it's waiting on a named thing), which is diagnosable in a way pure stall is not.
So diagnostics is less a recovery action and more an observability reflex: it makes the invisible state of a run legible. Stalled, stuck, or long-running — three words that turn "why is this thing taking forever?" into an answerable question. And once a system can inspect its own runs, the natural next step is letting it inspect — and carefully repair — its own configuration.
The reflexes so far heal runtime failures — a flaky call, a down provider, a full context. But some failures are baked into configuration: a risky default left on, a setting that should have been flipped, a tool that needs adjusting. The most ambitious self-healing reflex lets the agent inspect and repair its own config — carefully, through the gateway, with the brakes wired in.
The agent does this through a dedicated gateway tool rather than by editing files directly. Two operations matter. config.schema.lookup lets the agent read the configuration schema — ask "what settings exist, what do they mean, what are the valid values?" — before touching anything. And config.patch lets it change a specific setting through the same audited path. Look up first, then patch: the agent never pokes at a config it doesn't understand.
openclaw security audit --fix, scans the configuration for risky settings — defaults that are convenient but unsafe — and flips them back to their safe values. It's a one-shot hardening pass: find the loosened bolts and tighten them. Run it after setup, or whenever you suspect the config has drifted toward "convenient but exposed."
The word doing the heavy lifting in this whole chapter is guardrails. Letting an agent rewrite its own configuration is genuinely dangerous — a careless patch could open a security hole or break the gateway. So self-update runs are wrapped in the same machinery as everything else: they go through the audited tool path (not raw file edits), and updates run with status checks — the system verifies the change took effect and the gateway is still healthy, rather than blindly assuming success.
config.schema.lookup / config.patch and the audit command — instead of handing the agent a shell and a text editor — is that every change is legible and reversible. The agent can read the schema to know what's valid, patch one setting at a time through an audited seam, and let the audit pass undo anything risky. Self-healing config is powerful precisely because it is fenced: the agent gets to fix itself, but only along the rails the gateway lays down.
That's the fourth corner of self-healing filled in. Retry rides out a flicker; failover routes around a down provider; compaction recovers an overflowed context; diagnostics makes a frozen run legible; and self-update lets the agent harden its own config — each one fenced so the cure is never worse than the disease. Now let's watch all of them work together on one request.
config.schema.lookup / config.patch and the security audit --fix path instead of editing config files directly?Here is the whole lesson in one moving picture. A request flows left to right through the gateway's stages. Press a button to inject a failure mid-flight and watch the matching reflex take over: rate-limit triggers retry with backoff; provider down triggers failover walking the candidate chain; context overflow triggers compaction shrinking the transcript. The request ends green when a reflex recovers it, or amber when the gateway surfaces the failure with a reason. This diagram is the map of which reflex handles which failure.
The dot is your request, moving through intake → classify → provider → context → reply. Inject a failure at the provider or context stage and watch the reflex that owns it kick in — the dot loops back (retry), hops models (failover), or shrinks the context (compaction). Green = recovered; amber = surfaced with a reason.