← Writing

OpenClaw

When the Model Goes Down, How Does OpenClaw Keep Working?

After a rate limit, should the agent rotate credentials, switch models, or stop? Follow OpenClaw through failure classification, retry budgets, and session rollback.

Imagine the project checklist is almost finished.

OpenClaw has read the file, discussed the scope, and compacted the long conversation into a handover summary. You say, “Continue with that approach,” but the model service returns a rate-limit error. A little later, the assistant resumes, and the interface says it has switched to a fallback model.

It looks like a simple remedy: if A fails, try B.

But what if only the current account has exhausted its quota? Why change models immediately? If a local session-file lock caused the failure, what could B do differently? And if you manually selected C while waiting, is recovery allowed to quietly change the session back?

This article follows how OpenClaw classifies failures, chooses another attempt, and records the resulting selection. The checklist is hypothetical. The source is pinned to the v2026.5.22 commit, and local experiments use constructed data without calling live model services.

Another key, or another door?

“The model is unavailable” often hides the actual problem. A provider can have several authentication profiles, potentially representing different accounts or authentication methods with different quota and availability states.

OpenClaw separates recovery into two layers: profile rotation inside the current provider and an outer model-candidate chain. The first still targets the current model; the second can change the provider/model pair. This keeps an unusable credential from immediately being treated as an unavailable service. Profile ordering · Inner runner

Ordering does not mean randomly changing accounts on every request. Configured order, session preference, and cooldown state all influence selection. An automatically chosen profile can be a preference to try first, while a user-locked profile has stricter constraints. Describing rotation without its selection source confuses “choose one for me” with permission to override an explicit choice.

The model layer has a similar distinction. A default model can have fallbacks. A model explicitly selected for a session is an exact selection; failure should not silently produce an answer from an unrelated default candidate. Automatic fallback carries an auto source marker so later runs can identify it. Agent settings, cron jobs, and one-turn overrides have additional rules; this article concentrates on an ordinary session. Selection-source policy

The transferable design is to carry both the selected value and the reason it was selected. A model string alone cannot tell a downstream component whether it represents a default, an automatic recovery decision, or a user requirement that must remain exact.

Two recovery layers: rotate credentials within the current provider, then try another model candidate when policy permits
Selection constraints and failure causes determine the recovery layer. The diagram does not promise a model switch after every failure.

One status code can conceal several failures

The runner must translate varied provider errors into internal decisions.

FailoverError carries more than a message. It records a reason, provider, model, profile ID, and available status and error codes. Normalization searches wrapped error and cause objects for evidence instead of relying exclusively on the outermost “request failed” message. Error structure and normalization

Classification combines HTTP status, structured codes, and message content. There is no universal status-to-action table. A 400 or 422 response is not necessarily just a malformed request: its body may explicitly describe a rate limit, authentication failure, or billing problem. Conversely, an HTTP wrapper with no response body is not automatically classified as a format error. Missing evidence and a definite schema rejection are treated differently. Signal classifier · HTTP branches

Authentication failures differ in confidence too. An ambiguous “invalid api key” message should not automatically receive the same long unavailability treatment as an explicit revocation. Running the original matching module confirmed that the former can match an authentication error without matching the high-confidence permanent-revocation patterns. Authentication, quota, and timeout patterns

Cancellation has another trap. A library may use AbortError for a user stopping work, but it may also wrap a timeout or a structured rate limit in that error type. The candidate wrapper attempts normalization first. Explicit failure evidence is not discarded merely because the wrapper’s name contains Abort. Candidate execution wrapper

The cost is an expanding rule set whose ordering matters. One message might contain both “internal error” and “insufficient balance”; whichever rule wins changes the next action. Classification deserves a behavioral contract and a growing set of conflicting examples, rather than scattered string checks throughout the runtime.

Some failures cannot be fixed by ten more models

Return to the checklist. If another execution has taken over the session file, model B inherits the same local coordination problem. Walking more candidates wastes time and can disguise the final result as “all models failed.”

This version’s outer fallback recognizes local coordination failures without provider-failure evidence, including session write-lock timeouts and embedded-attempt takeover, and rethrows them. Context overflow escaping to the outer layer is likewise returned to the inner compaction and recovery path. Switching to another model, potentially with a smaller window, is not a universal remedy. Outer stopping conditions

This connects directly to the previous article: excessive context calls for working on the input; temporary provider unavailability calls for considering another route. The layer where a failure originates should influence the layer responsible for recovery.

The opposite claim—“only recognized errors can try a fallback”—would also be inaccurate. The actual outer loop allows some unrecognized errors to continue while candidates remain. Explicit stopping conditions are checked first. Other unknown errors can be recorded before the next attempt; an unrecognized error at the final candidate is rethrown. Exhaustion of classified failures can instead produce a FallbackSummaryError carrying the individual attempts. Unknown errors and exhaustion

The policy is therefore an ordered decision process. Protect cancellation and local-state boundaries first, determine whether another candidate is available, and preserve why each attempt failed.

Fallback is not permission to retry forever

The candidate chain normalizes model references, collects candidates, and deduplicates provider/model pairs. An explicitly supplied fallback list replaces the default even when empty. An empty list means no further fallback; treating it as missing configuration would undo that choice. Candidate construction

The outer candidate count is not the total retry count. One candidate can involve credential rotation or recovery branches such as compaction. OpenClaw therefore gives the inner run loop an additional defensive limit. Its defaults in this version can be summarized as:

limit = min(160, max(32, 24 + max(1, profileCount) × 8))

Configuration can override these defaults. We verified limits of 32 for one profile, 40 for two, and a cap of 160 for many profiles. These are inner-loop iteration limits. They do not mean the model will definitely be called 32 times, nor are they a complete request-cost budget or a strict total-duration guarantee. Run-loop guard

Failed profiles also need to step aside temporarily. The ordinary cooldown function returns 30 seconds, one minute, and five minutes as error counts increase, then caps there. Billing and high-confidence permanent authentication failures follow a separate, longer disabled policy. A familiar exponential-backoff formula would not accurately describe all of this version’s errors. Cooldown and disabled policies

A useful detail appears in the deadline update. While an ordinary cooldown window remains active, the update path preserves its existing expiry instead of extending it after each failure within that window. Once it expires, the deadline can be recomputed. Otherwise, frequent probes could keep pushing an unhealthy credential’s cooldown into the future. Active-window handling

Cooldown is scheduling state, not an instruction to put the user’s request to sleep for five minutes. The outer layer combines profile availability and restricted probing to decide whether to skip or attempt a candidate. Credential cooldown, model candidates, and inner-loop limits constrain different resources. Understanding how long one task actually waited requires viewing them together in its execution trace.

Fallback state handling: record candidate selection before execution, then roll back only fields still matching that attempt’s writes
Loop budgets constrain attempts; field ownership constrains rollback. Both limit what recovery is allowed to do.

When B takes over, whom should the session remember?

Suppose A really is rate-limited and B can continue the checklist. Switching is more than changing a model argument. The session also records which model it is currently using.

For an ordinary persisted session, the execution wrapper records a fallback selection before starting that candidate when the relevant conditions hold. The fields include provider/model, the automatic source marker, and the originating model. Other session readers should not see A while the runner is already requesting B. User-sourced and one-turn overrides have guards; persistence is not unconditional. Persisting candidate selection

What if B fails too? Restoring an entire pre-attempt session snapshot would be dangerous. New state, a session rotation, or a model the user selected while waiting could disappear with it.

OpenClaw’s rollback function visits only the selection fields it owns. For each field, it restores the old value only when the current value still matches what that fallback attempt expected to write. A field that has changed is left alone. Field-wise rollback

In our constructed experiment, we changed the model field to a new user choice and changed an unrelated session ID before rolling back. Both new values survived. But the comparison is field-wise: an unchanged provider field can still revert. This is not an atomic compare-and-swap on the complete provider/model tuple. The local checks establish the function’s behavior, not full session consistency under every concurrent interleaving.

That detail reveals more than a feature label such as “automatic fallback.” Recovery also modifies shared state. It needs to know which fields belong to it and which changes happened afterward.

Taking the design elsewhere

I would begin with three interfaces: a classifier returning causes and evidence, a policy choosing stop, profile rotation, or model fallback, and a state layer recording selections and conditional rollback. Model execution remains separate. It should neither add candidates secretly nor present a local lock failure as a provider outage.

Verification should follow those boundaries: conflicting error examples for classification, a deterministic clock for cooldown, a small candidate set for stopping conditions, and concurrent scenarios for state ownership. This article’s 30 local checks cover error-text matching, cooldown windows, iteration limits, and in-memory field rollback. The complete structured classifier, live provider failures, full model chain, and persisted concurrency were inspected in source rather than reproduced end to end.

Finally, recovering a model request does not undo external actions. If a tool has already produced a side effect, changing models does not provide idempotency for it. Whether the checklist was saved must be established from tool results and file state, not inferred from a successful fallback.

The user may see only a brief notice that a backup model has taken over. The useful design underneath is to give every new attempt a reason and a boundary, while preserving the decisions the user made as the system was trying to recover.

继续阅读