Model Failover and Auth Profile Rotation in OpenClaw
This page explains how OpenClaw handles model failures through auth profile rotation and model fallback. It is intended for developers configuring agent reliability and failover policies.
Read this when
- Diagnosing auth profile rotation, cooldowns, or model fallback behavior
- Updating failover rules for auth profiles or models
- Understanding how session model overrides interact with fallback retries
OpenClaw handles failures in two stages:
- Auth profile rotation within the current provider.
- Model fallback to the next model in
agents.defaults.model.fallbacks.
Runtime flow
Resolve session state
Resolve the active session model and auth-profile preference.
Build candidate chain
Build the model candidate chain from the current model selection and the fallback policy for that selection source. Configured defaults, cron job primaries, and auto-selected fallback models can use configured fallbacks; explicit user session selections are strict.
Try the current provider
Try the current provider with auth-profile rotation/cooldown rules.
Advance on failover-worthy errors
If that provider is exhausted with a failover-worthy error, move to the next model candidate.
Use fallback for the current turn
Run the winning fallback candidate without changing the session's selected provider/model.
Retry safe pure overload exhaustion
If every candidate fails only because providers are overloaded, retry the full turn-local chain up to 10 times with exponential backoff while no tool execution or assistant output has started. After 30 seconds, send one status notice so the user is not left waiting silently.
Throw FallbackSummaryError if exhausted
If every candidate fails, throw a FallbackSummaryError with per-attempt detail and the soonest cooldown expiry when one is known.
Fallback execution is turn-local. The reply runner persists only fallback notice state so /status and transition notices can distinguish the selected model from the model that answered; it does not persist the fallback as the next turn's model selection.
Selection source policy
The selection source controls whether the fallback chain is allowed:
- Configured default:
agents.defaults.model.primaryusesagents.defaults.model.fallbacks. - Agent primary:
agents.entries.*.modelis strict unless that agent's model object includes its ownfallbacks. Usefallbacks: []to make the strict behavior explicit, or a non-empty list to opt that agent into model fallback. - Runtime fallback: the fallback candidate applies only to the current turn. The next turn starts from the selected primary again. OpenClaw still recognizes previously stored
modelOverrideSource: "auto"entries, probes their configured origin every 5 minutes, and clears them once the origin recovers./new,/reset, andsessions.resetalso clear those entries. - User session override:
/model, the model picker,session_status(model=...), andsessions.patchwritemodelOverrideSource: "user". This is an exact session selection. If the selected provider/model fails before producing a reply, OpenClaw reports the failure instead of answering from an unrelated configured fallback. - Legacy session override: older session entries may have
modelOverridewithoutmodelOverrideSource. OpenClaw treats those as user overrides so an explicit old selection is not silently converted into fallback behavior. - Cron payload model: a cron job
payload.model/--modelis a job primary, not a user session override. It uses configured fallbacks unless the job providespayload.fallbacks;payload.fallbacks: []makes the cron run strict.
OpenClaw sends a visible notice when a turn moves onto fallback and another notice when a later turn succeeds on the selected primary. Persisted notice state prevents repeated notices when consecutive turns use the same selected/active pair, while model selection itself remains unchanged.
Auth failure skip cache
By default, every new turn keeps the existing fallback retry behavior: OpenClaw retries each configured fallback candidate again, including non-primary candidates that recently failed with auth or auth_permanent.
Opt in to suppress repeat auth failures with:
OPENCLAW_FALLBACK_SKIP_TTL_MS=60000
When enabled, OpenClaw records an in-memory, session-scoped skip marker for a non-primary fallback candidate after an auth-class failure, keyed by session id, provider, and model. Primary candidates are never skipped, so an explicit user model selection still surfaces the real auth error. The cache is process-local and clears on Gateway restart.
The value is a TTL in milliseconds. 0 or unset disables the cache. Positive values are clamped between 1 second and 10 minutes.
User-visible fallback notices
When a session moves onto an auto-selected fallback, OpenClaw sends a status notice in the same reply surface:
↪️ Model Fallback: <fallback> (selected <primary>; <reason>)
When a later probe succeeds and the session returns to the selected primary, OpenClaw sends:
↪️ Model Fallback cleared: <primary> (was <fallback>)
These notices are operational messages, not assistant content. They deliver once per state change, including side-effect-only turns when feasible, but repeated turn-local fallback transitions do not repeat them. Delivery bypasses normal source-reply suppression, does not consume the first assistant reply slot for threaded channels, and is excluded from text-to-speech and commitment extraction.
Auth storage (keys + OAuth)
OpenClaw uses auth profiles for both API keys and OAuth tokens.
- Secrets and runtime auth-routing state live in
~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite. - Config
auth.profiles/auth.orderare metadata + routing only (no secrets). - Legacy import-only OAuth file:
~/.openclaw/credentials/oauth.json(imported into the per-agent auth store on first use). - Legacy
auth-profiles.json,auth-state.json, and per-agentauth.jsonfiles are imported byopenclaw doctor --fix.
More detail: OAuth
Credential types:
type: "api_key"→{ provider, key }type: "oauth"→{ provider, access, refresh, expires, email? }(+projectId/enterpriseUrlfor some providers)type: "token"→ static bearer-style token, optionally expiring; OpenClaw does not refresh it (used foraws-sdkand other credential-chain auth modes)
Profile IDs
OAuth logins create distinct profiles so multiple accounts can coexist.
- Default:
provider:defaultwhen no email is available. - OAuth with email:
provider:<email>(for examplegoogle-antigravity:user@gmail.com).
Profiles live in the per-agent openclaw-agent.sqlite auth profile store.
Rotation order
When a provider has multiple profiles, OpenClaw chooses an order like this:
Explicit config
auth.order[provider] (if set).
Configured profiles
auth.profiles filtered by provider.
Stored profiles
Per-agent SQLite auth profile entries for the provider.
If no explicit order is configured, OpenClaw uses a round-robin order:
- Primary key: profile type (OAuth, then static token, then API key).
- Secondary key for OAuth: profiles with a currently usable access token before profiles whose access token is expired. Expired OAuth profiles stay eligible so the runtime can refresh them when no usable peer is available.
- Next key:
usageStats.lastUsed(oldest first, within each type/state tier). - Cooldown/disabled profiles are moved to the end, ordered by soonest expiry.
Session stickiness (cache-friendly)
OpenClaw pins the chosen auth profile per session to keep provider caches warm. It does not rotate on every request. The pinned profile is reused until:
- the session is reset (
/new//reset) - a compaction completes (compaction count increments)
- the profile is in cooldown/disabled
Manual selection via /model …@<profileId> sets a user override for that session and is not auto-rotated until a new session starts.
Note
Auto-pinned profiles (selected by the session router) are treated as a preference: they are tried first, but OpenClaw may rotate to another profile on rate limits/timeouts. When the original profile becomes available again, new runs can prefer it again without changing the selected model or runtime. User-pinned profiles stay locked to that profile; if it fails and model fallbacks are configured, OpenClaw moves to the next model instead of switching profiles.
OpenAI Codex subscription plus API-key backup
For OpenAI agent models, auth and runtime are separate. openai/gpt-* stays on the Codex harness while auth can rotate between a Codex subscription profile and an OpenAI API-key backup.
Use auth.order.openai for the user-facing order:
{
auth: {
order: {
openai: ["openai:user@example.com", "openai:api-key-backup"],
},
},
}
Use openai:* for both ChatGPT/Codex OAuth profiles and OpenAI API-key profiles. When the subscription hits a Codex usage limit, OpenClaw records the exact reset time when Codex provides one, tries the next ordered auth profile, and keeps the run inside the Codex harness. Once the reset time passes, the subscription profile is eligible again and the next automatic selection can return to it.
Use a user-pinned profile only when you want to force one account/key for that session. User-pinned profiles are intentionally strict and do not silently jump to another profile.
Cooldowns
When a profile fails due to auth/rate-limit errors (or a timeout that looks like rate limiting), OpenClaw marks it in cooldown and moves to the next profile.
What lands in the rate-limit / timeout bucket
That rate-limit bucket covers more than just plain 429: it also includes provider messages like Too many concurrent requests, ThrottlingException, concurrency limit reached, workers_ai ... quota limit exceeded, throttled, resource exhausted, and periodic usage-window limits such as weekly limit reached or monthly limit exhausted.
Format and invalid-request errors are typically terminal because resending the same payload would produce the same failure, so OpenClaw surfaces them instead of cycling auth profiles. Known retry-repair paths can opt in explicitly: for example, Cloud Code Assist tool call ID validation failures are sanitized and retried once through the allowFormatRetry policy.
OpenAI-compatible provider-completed stop and finish reasons such as Unhandled stop reason: error, stop reason: error, reason: error, and Provider finish_reason: error are categorized as server_error (HTTP-like status 500), not timeout. They remain eligible for failover during model or profile rotation, but diagnostics keep the provider's finish-reason text instead of replacing the user copy with "LLM request timed out." Transport-shaped finish reasons such as Provider finish_reason: abort, network_error, and malformed_response stay in the timeout and failover bucket (status 408).
Generic server text can also end up in that timeout bucket when the source matches a known transient pattern. For example, the bare model runtime stream-wrapper message An unknown error occurred is treated as failover-worthy for every provider because the shared model runtime emits it when provider streams end with stopReason: "aborted" or stopReason: "error" without specific details. JSON api_error payloads with transient server text such as internal server error, unknown error, 520, upstream error, or backend error are also treated as failover-worthy timeouts.
OpenRouter-specific generic upstream text such as bare Provider returned error is treated as timeout only when the provider context is actually OpenRouter. Generic internal fallback text such as LLM request failed with an unknown error. stays conservative and does not trigger failover by itself.
SDK retry-after caps
Some provider SDKs might otherwise sleep for a long Retry-After window before returning control to OpenClaw. For Stainless-based SDKs such as Anthropic and OpenAI, OpenClaw caps SDK-internal retry-after-ms and retry-after waits at 60 seconds by default and surfaces longer retryable responses immediately so this failover path can run. Tune or disable the cap with OPENCLAW_SDK_RETRY_MAX_WAIT_SECONDS; see Retry behavior.
Model-scoped cooldowns
Rate-limit cooldowns can also be model-scoped:
- OpenClaw records
cooldownModelfor rate-limit failures when the failing model id is known. - A sibling model on the same provider can still be tried when the cooldown is scoped to a different model.
- Billing and disabled windows still block the whole profile across models.
Regular cooldowns (not billing, not auth-permanent) scale with the profile's recent error count:
- 1st failure: 30 seconds
- 2nd failure: 1 minute
- 3rd+ failure: 5 minutes (cap)
Counters reset once the profile's built-in failure window has passed.
State is stored in the per-agent SQLite auth state under usageStats:
{
"usageStats": {
"provider:profile": {
"lastUsed": 1736160000000,
"cooldownUntil": 1736160600000,
"errorCount": 2
}
}
}
Billing disables
Billing and credit failures (for example "insufficient credits" or "credit balance too low") are treated as failover-worthy, but they are usually not transient. Instead of a short cooldown, OpenClaw marks the profile as disabled (with a longer backoff) and rotates to the next profile or provider.
Note
Not every billing-shaped response is
402, and not every HTTP402lands here. OpenClaw keeps explicit billing text in the billing lane even when a provider returns401or403instead, but provider-specific matchers stay scoped to the provider that owns them (for example OpenRouter403 Key limit exceeded).Meanwhile temporary
402usage-window and organization or workspace spend-limit errors are classified asrate_limitwhen the message looks retryable (for exampleweekly usage limit exhausted,daily limit reached, resets tomorrow, ororganization spending limit exceeded). Those stay on the short cooldown and failover path instead of the long billing-disable path.
High-confidence permanent-auth failures (revoked or deactivated keys, deactivated workspaces) get a similar disabled lane, but recover much sooner than billing since some providers surface auth-looking payloads transiently during incidents.
State is stored in the per-agent SQLite auth state:
{
"usageStats": {
"provider:profile": {
"disabledUntil": 1736178000000,
"disabledReason": "billing"
}
}
}
Overloaded and rate-limit errors are handled more aggressively than billing cooldowns: by default, OpenClaw allows one same-provider auth-profile retry, then switches to the next configured model fallback without waiting.
Model fallback
If all profiles for a provider fail, OpenClaw moves to the next model in agents.defaults.model.fallbacks. This applies to auth failures, rate limits, and timeouts that exhausted profile rotation (other errors do not advance fallback). Provider errors that do not expose enough detail are still labeled precisely in fallback state: empty_response means the provider returned no usable message or status, no_error_details means the provider explicitly returned Unknown error (no error details in response), and unclassified means OpenClaw preserved the raw preview but no classifier matched it yet.
Provider-busy signals such as ModelNotReadyException land in the overloaded bucket and follow the same one-rotation-then-fallback policy as rate limits (see the defaults table above).
If the entire candidate chain is exhausted only by overload failures, the reply runner retries the chain up to 10 times in the same turn. Full-turn retry is allowed only before tool execution or assistant output starts, avoiding duplicate mutations or messages if an overload arrives after observable work. Backoff starts at 2.5 seconds and doubles to a 30-second cap. Once the turn has been waiting for 30 seconds, OpenClaw sends one transient status notice: The AI service is temporarily overloaded. I’m still retrying; this may take a few minutes. The retry and any fallback winner remain turn-local; ordinary transient server errors retain their separate one-retry policy.
When a run starts from the configured default primary, a cron job primary, an agent primary with explicit fallbacks, or an auto-selected fallback override, OpenClaw can walk the matching configured fallback chain. Agent primaries without explicit fallbacks and explicit user selections (for example /model ollama/qwen3.5:27b, the model picker, sessions.patch, or one-off CLI provider and model overrides) are strict: if that provider or model is unreachable or fails before producing a reply, OpenClaw reports the failure instead of answering from an unrelated fallback.
Candidate chain rules
OpenClaw builds the candidate list from the currently requested provider/model plus configured fallbacks.
Rules
- The requested model is always first.
- Explicit configured fallbacks are deduplicated but not filtered by the model allowlist. They are treated as explicit operator intent.
- If the current run is already on a configured fallback in the same provider family, OpenClaw keeps using the full configured chain.
- When no explicit fallback override is supplied, configured fallbacks are tried before the configured primary even if the requested model uses a different provider.
- When no explicit fallback override is supplied to the fallback runner, the configured primary is appended at the end so the chain can settle back onto the normal default once earlier candidates are exhausted.
- When a caller supplies
fallbacksOverride, the runner uses exactly the requested model plus that override list. An empty list disables model fallback and prevents the configured primary from being appended as a hidden retry target.
Which errors advance fallback
Continues on
- auth failures
- rate limits and cooldown exhaustion
- overloaded and provider-busy errors
- timeout-shaped failover errors
- billing disables
LiveSessionModelSwitchError, which is normalized into a failover path so a stale persisted model does not create an outer retry loop- other unrecognized errors when there are still remaining candidates
Does not continue on
- explicit aborts that are not timeout or failover-shaped
- context overflow errors that should stay inside compaction and retry logic (for example
request_too_large,input token count exceeds the maximum number of input tokens,input exceeds the maximum number of tokens,input too long for the model, orollama error: context length exceeded) - a final unknown error when there are no candidates left
- Claude Fable 5 safety refusals; direct API-key requests handle those at the provider level via Anthropic's server-side fallback to
claude-opus-4-8instead (see Anthropic)
Cooldown skip vs probe behavior
When every auth profile for a provider is already in cooldown, OpenClaw does not automatically skip that provider forever. It makes a per-candidate decision:
Per-candidate decisions
- Persistent auth failures skip the whole provider immediately.
- Billing disables usually skip, but the primary candidate can still be probed on a throttle so recovery is possible without restarting.
- The primary candidate may be probed near cooldown expiry, with a per-provider throttle.
- Same-provider fallback siblings can be attempted despite cooldown when the failure looks transient (
rate_limit,overloaded, or unknown). This is especially relevant when a rate limit is model-scoped and a sibling model may still recover immediately. - Transient cooldown probes are limited to one per provider per fallback run so a single provider does not stall cross-provider fallback.
Session overrides and live model switching
Session model changes are shared state. The active runner, /model command, compaction and session updates, and live session reconciliation all read or write parts of the same session entry. Fallback execution does not write model selection fields, so it cannot replace a newer manual selection while retrying.
Live model switching follows these rules:
- Only explicit user driven model changes mark a pending live switch. This includes
/model,session_status(model=...), andsessions.patch. - System driven model changes such as fallback rotation, heartbeat overrides, or compaction never mark a pending live switch on their own.
- User driven model overrides are treated as exact selections for fallback policy, so an unreachable selected provider surfaces as a failure instead of being masked by
agents.defaults.model.fallbacks. - Runtime fallback candidates remain turn local. The next turn starts from the current selected model, including a manual selection that arrived during the previous run.
- Previously stored auto fallback overrides remain supported: OpenClaw periodically probes their configured origin and clears the override when it recovers;
/new,/reset, andsessions.resetclear auto sourced overrides immediately. - User replies announce fallback transitions and fallback cleared recovery once per state change. Repeated turns with the same selected and active pair do not repeat the notice.
/statusshows the selected model and, when fallback state differs, the active fallback model and reason.- Live session reconciliation prefers persisted session overrides over stale runtime model fields.
- If a live switch error points at a later candidate in the active fallback chain, OpenClaw jumps directly to that selected model instead of walking unrelated candidates first.
The active run carries its chosen candidate directly. Live reconciliation changes that candidate only for an explicit pending user switch, so no temporary fallback override or rollback is needed.
Observability and failure summaries
runWithModelFallback(...) records per attempt details that feed logs and user facing cooldown messaging:
- provider and model attempted
- reason (
rate_limit,overloaded,billing,auth,model_not_found, and similar failover reasons) - optional status or code
- human readable error summary
Structured model_fallback_decision logs also include flat fallbackStep* fields when a candidate fails, is skipped, or a later fallback succeeds. These fields make the attempted transition explicit (fallbackStepFromModel, fallbackStepToModel, fallbackStepFromFailureReason, fallbackStepFromFailureDetail, fallbackStepFinalOutcome) so log and diagnostic exporters can reconstruct the primary failure even when the terminal fallback also fails.
When every candidate fails, OpenClaw throws FallbackSummaryError. The outer reply runner can use that to build a more specific message such as "all models are temporarily rate limited" and include the soonest cooldown expiry when one is known.
That cooldown summary is model aware:
- unrelated model scoped rate limits are ignored for the attempted provider and model chain
- if the remaining block is a matching model scoped rate limit, OpenClaw reports the last matching expiry that still blocks that model
Related config
See Gateway configuration for:
auth.profiles/auth.orderagents.defaults.model.primary/agents.defaults.model.fallbacksagents.defaults.imageModelrouting
See Models for the broader model selection and fallback overview.