#13188: fix: add cross-provider fallback when primary provider is rate-limited
agents
stale
## Summary
When all models from the primary provider are exhausted or rate-limited, the fallback chain now includes models from OTHER configured providers.
This addresses issue #13082 where users with Anthropic-only fallbacks couldn't escape to OpenRouter when Anthropic returned 429.
## Changes
- `resolveFallbackCandidates()` now appends one model from each configured provider (in `models.providers`) that isn't already in the fallback chain
- Added test case verifying cross-provider escape on rate limit
## Before
```
Primary: anthropic/claude-sonnet-4
Fallbacks: [anthropic/claude-opus-4]
When Anthropic returns 429:
1. Try claude-sonnet-4 → 429
2. Try claude-opus-4 → 429
3. Error: All models failed
```
## After
```
Primary: anthropic/claude-sonnet-4
Fallbacks: [anthropic/claude-opus-4]
Configured providers: { openrouter: { models: [...] } }
When Anthropic returns 429:
1. Try claude-sonnet-4 → 429
2. Try claude-opus-4 → 429
3. Try openrouter/first-model → Success! (cross-provider escape)
```
## Test Plan
- [x] All existing tests pass (19/19)
- [x] New test case for cross-provider fallback passes
- [ ] Manual testing with real rate-limited scenario
Fixes #13082
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR extends `resolveFallbackCandidates()` so that when the primary provider (and its configured fallbacks) are exhausted/rate-limited, the fallback chain can “escape” to at least one model from other configured providers in `cfg.models.providers`. A new Vitest test exercises the Anthropic->OpenRouter cross-provider fallback path on 429s.
Main integration point is `src/agents/model-fallback.ts`, which constructs the ordered candidate list consumed by `runWithModelFallback()`. The added logic appends at most one model per extra provider after the existing primary/fallbacks/configured-primary candidates.
<h3>Confidence Score: 3/5</h3>
- This PR is close, but has a couple of correctness gaps in provider normalization and test realism that should be fixed before merging.
- The functional intent is clear and the change is localized, but (1) using raw `models.providers` keys as provider IDs can break allowlist/cooldown matching when keys aren’t normalized, and (2) the new test uses an invalid `ModelProviderConfig.models` shape that may not reflect real validated configs.
- src/agents/model-fallback.ts, src/agents/model-fallback.test.ts
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#11349: fix(agents): do not filter fallback models by models allowlist
by liuxiaopai-ai · 2026-02-07
83.1%
#13626: fix(model): propagate provider model properties in fallback resolution
by mcaxtr · 2026-02-10
82.9%
#23816: fix(agents): model fallback skipped during session overrides and pr...
by ramezgaberiel · 2026-02-22
81.9%
#16838: fix: include configured fallbacks in model allowlist
by taw0002 · 2026-02-15
81.0%
#7570: fix: allow models from providers with auth profiles configured
by DonSqualo · 2026-02-03
80.8%
#22064: fix(failover): bypass models allowlist for configured fallback models
by winston-bepresent · 2026-02-20
80.6%
#13077: fix: prevent cooldown pollution across different models on the same...
by magendary · 2026-02-10
79.9%
#9427: fix: trigger model fallback on all 4xx HTTP errors
by dbottme · 2026-02-05
79.2%
#14744: fix(context): key MODEL_CACHE by provider/modelId to prevent collis...
by lailoo · 2026-02-12
78.8%
#14508: fix(models): allow forward-compat models in allowlist check
by jonisjongithub · 2026-02-12
78.3%