Area
Provider adapters
What are you trying to accomplish?
Use OpenRouter free-tier models (openrouter/minimax/minimax-m3:free, openrouter/openrouter/free, etc.) behind a combo/failover target without manual intervention when a key's weekly free quota is exhausted (OpenRouter replies with 429 rate_limit_error, Weekly/Monthly Limit Exhausted... will reset at <date>).
What prevents this today?
OCX already has two related mechanisms:
apiKeyPool multi-key failover (src/providers/key-failover.ts): on a 429, rotateKeyOn429() moves to the next key in the pool and puts the exhausted key into cooldown (default 60 s, cap 10 min, Retry-After respected).
- Combo failover across targets: a combo can fail over to another provider/model.
The gap for the OpenRouter free-tier case specifically:
- The free-quota reset horizon is weekly (
Your limit will reset at 2026-09-09 03:30:06), but MAX_COOLDOWN_MS caps a key's cooldown at 10 minutes. After 10 minutes the exhausted key becomes active again, and roughly every other request goes to a dead key, failing with 429 and only then rotating — the wasted attempt repeats all week until the quota actually resets.
- The OpenRouter error body carries the exact reset timestamp (
will reset at <date>), but the cooldown logic appears to only honor the Retry-After HTTP header; the body-parsed reset date is not used to extend the cooldown.
- There is no per-key quota-state notion (e.g.,
exhausted_until: <ts>), so an exhausted key cannot be parked for the rest of the window the upstream itself declares.
What should OpenCodex do?
- Parse the OpenRouter 429 JSON body for
rate_limit_error and extract the reset timestamp from the message; park the key in cooldown until that timestamp (bounded, e.g. up to 7 days, configurable).
- Optionally: a generic
quotaExhaustedCooldownMs / quotaExhaustedUntilReset: true knob under retryOn429 (or a sibling policy) so other providers that report reset windows in the error body get the same behavior.
- With a single key in the pool, the same detection should feed combo-level failover: mark the target as quota-exhausted so the combo routes to the next target for the remainder of the window instead of burning one failed attempt per request.
Example usage or interface
{
"providers": {
"openrouter": {
"authMode": "key",
"apiKeyPool": [
{ "id": "key-a", "key": "sk-or-..." },
{ "id": "key-b", "key": "sk-or-..." }
],
"retryOn429": {
"enabled": true,
"quotaResetFromBody": true,
"maxQuotaCooldownMs": 604800000
}
}
}
}
Expected behavior: after the upstream answers 429 Weekly/Monthly Limit Exhausted ... will reset at 2026-09-09 03:30:06, key-a is parked until that timestamp and all traffic goes to key-b (or the combo's next target) without per-request 429 tax.
Alternatives or workarounds
- Manually pausing/removing the exhausted key in the dashboard until the reset date.
- Using a short combo retry budget so every request pays one 429 before failing over (current de-facto behavior; wasteful on long windows).
- External cron that swaps keys in the config when the weekly reset hits.
Checks
Area
Provider adapters
What are you trying to accomplish?
Use OpenRouter free-tier models (
openrouter/minimax/minimax-m3:free,openrouter/openrouter/free, etc.) behind a combo/failover target without manual intervention when a key's weekly free quota is exhausted (OpenRouter replies with 429rate_limit_error,Weekly/Monthly Limit Exhausted... will reset at <date>).What prevents this today?
OCX already has two related mechanisms:
apiKeyPoolmulti-key failover (src/providers/key-failover.ts): on a 429,rotateKeyOn429()moves to the next key in the pool and puts the exhausted key into cooldown (default 60 s, cap 10 min,Retry-Afterrespected).The gap for the OpenRouter free-tier case specifically:
Your limit will reset at 2026-09-09 03:30:06), butMAX_COOLDOWN_MScaps a key's cooldown at 10 minutes. After 10 minutes the exhausted key becomes active again, and roughly every other request goes to a dead key, failing with 429 and only then rotating — the wasted attempt repeats all week until the quota actually resets.will reset at <date>), but the cooldown logic appears to only honor theRetry-AfterHTTP header; the body-parsed reset date is not used to extend the cooldown.exhausted_until: <ts>), so an exhausted key cannot be parked for the rest of the window the upstream itself declares.What should OpenCodex do?
rate_limit_errorand extract the reset timestamp from the message; park the key in cooldown until that timestamp (bounded, e.g. up to 7 days, configurable).quotaExhaustedCooldownMs/quotaExhaustedUntilReset: trueknob underretryOn429(or a sibling policy) so other providers that report reset windows in the error body get the same behavior.Example usage or interface
{ "providers": { "openrouter": { "authMode": "key", "apiKeyPool": [ { "id": "key-a", "key": "sk-or-..." }, { "id": "key-b", "key": "sk-or-..." } ], "retryOn429": { "enabled": true, "quotaResetFromBody": true, "maxQuotaCooldownMs": 604800000 } } } }Expected behavior: after the upstream answers 429
Weekly/Monthly Limit Exhausted ... will reset at 2026-09-09 03:30:06, key-a is parked until that timestamp and all traffic goes to key-b (or the combo's next target) without per-request 429 tax.Alternatives or workarounds
Checks