Repository navigation
Description
Feature hasn't been suggested before.
- I have verified this feature I'm about to request hasn't been suggested before.
Describe the enhancement you want to request
Supersedes #7602 (Jan 2026) and the closed PR #20105 (Mar 2026, auto-cleaned). Incorporates lessons learned from #20105 and aligns with the current Effect-based session architecture.
Problem
When a model is unreachable, rate-limited, or times out, the session retry policy (session/retry.ts) retries the same model on the same provider with exponential backoff. After retries are exhausted, halt() is called and the session dies. There is no mechanism to fall back to a different model.
This causes long-running agent workflows to fail on transient provider issues that a different provider would handle fine.
Proposed Behavior
After the retry policy exhausts for a transient error (5xx, 429, network failure, timeout), instead of halting the session:
- Look up the configured fallback model(s) for the current model
- If a fallback exists and is available, switch to it and re-run the LLM stream
- Track which fallback was used for status reporting
- Emit a status event so the UI can show the model switch
- If the fallback also fails, try the next in the chain
- If all fallbacks are exhausted, halt as before
Triggers
- Rate limits (429, Too Many Requests)
- Server errors (500, 502, 503)
- Network failures (ECONNREFUSED, ECONNRESET, ETIMEDOUT, fetch failed)
- Timeouts (header timeout, stream timeout — the primary use case)
- Model unavailable (404, model not found)
Not triggered by:
- Auth errors (401 — different provider has different creds)
- Context overflow (413 — needs compaction, not a model switch)
- Validation/prompt errors
Config Schema
Aligns with the existing provider.models config structure:
{
"provider": {
"anthropic": {
"models": {
"claude-sonnet-4": {
"fallback": ["openai/gpt-5", "google/gemini-3-pro"]
}
}
},
"openai": {
"models": {
"gpt-5": {
"fallback": ["anthropic/claude-sonnet-4", "google/gemini-3-pro"]
}
}
}
}
}Each entry in the fallback array is a "provider/model-id" string. The array defines an ordered fallback chain — tried in sequence until one succeeds.
Architectural Notes
PR #20105 implemented fallback as LLM middleware via wrapLanguageModel. This approach has two issues:
- Interacts badly with the session retry loop — middleware runs inside the AI SDK's own retry, which conflicts with the Effect-based
SessionRetry.policy - Only 1 fallback — hardcoded Copilot-to-Bedrock mapping, not a configurable chain
The recommended approach is to implement fallback at the session processor layer (processor.ts), after Effect.retry(SessionRetry.policy(...)) exhausts:
Effect.retry(SessionRetry.policy(...))
-> Effect.catch(fallback) // instead of Effect.catch(halt)
-> Effect.catch(halt) // if all fallbacks exhausted
This keeps fallback logic in the Effect-based session pipeline where the error context is richest.
References
- [FEATURE]: Native Model Fallback / Failover Support #7602 — Original feature request (Jan 2026)
- feat(provider): automatic model fallback on transient errors #20105 — Closed PR implementing Copilot-to-Bedrock fallback (Mar 2026, auto-cleaned)
- [FEATURE]:Auto-Failover for Rate Limit Resilience #42513 — Related: API key pooling per provider
- [FEATURE]: Per-Provider Quota-Aware Auto-Retry with Configurable Reset Schedule #43324 — Related: Per-provider quota-aware retry
Activity
fix(hooks): treat tsgo OOM crashes as non-blocking in pre-push
fix(provider): restore schema + processor integration, revert pre-pus…
feat(provider): configurable model fallback on transient errors and t…
feat(provider): configurable model fallback on transient errors and t…
hd-o commentedon Sep 16, 2026
How about setting the models on top-level or agent?
{ "model": [ "anthropic/claude-sonnet-5", "openai/gpt-5" ] }{ "agent": { "plan": { "model": [ "anthropic/claude-sonnet-5", "openai/gpt-5" ] } } }tea0112 commentedon Oct 22, 2026
I need this because I have many providers — why bother respecting rate limits when we can automatically switch to another?
opencodejr commentedon Oct 28, 2026
Thanks for the detailed write-up, @AndyS77! You're asking for a configurable, ordered chain of fallback models per model. When retries on the current model run out for a transient error (rate limit, 5xx, network failure or timeout), OpenCode would switch to the next model in the chain instead of stopping the session, and show the switch in the UI.
Today v2 retries the same model with backoff and then stops. The plugin
session.retryhook can only decide whether to retry and how long to wait, not which model to use. So this can't be built cleanly as a plugin yet. A small addition would make it possible: letting the retry decision also name a replacement model for the next attempt.Maintainers will consider this request. Related issues, which you already mentioned:
moritzWa commentedon Oct 30, 2026
Use case that isn't covered here: the fallback also needs to fire when the provider's auth refresh gets throttled or its credits/quota run out ("The usage limit has been reached"), not only on 5xx/429/timeouts. Same model on another provider has separate creds, so 401 is fine to fall back on when the subscription itself is the problem.
Also useful: once a provider reports it's out of quota, skip it for a while (I use 30 min) instead of retrying it on every turn. Without that, every chat in a fallback chain burns a failed attempt on the dead provider first.
I run this today as a local plugin (ordered chain, cooldown on exhausted providers) and can port it to core if the processor-layer approach in the description is still the plan.