Repository navigation
[FEATURE]: Per-Provider Quota-Aware Auto-Retry with Configurable Reset Schedule #43324
Copy link
Copy link
Open
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
Problem
OpenCode's session retry already parses retry-after headers and recognises FreeUsageLimitError / GoUsageLimitError (see packages/opencode/src/session/retry.ts), but the handling is essentially hardcoded to the OpenCode Go subscription:
GoUsageLimitErroralready extractsmetadata.limitName("5 hour" | "weekly" | "monthly") andretry-after, then surfaces it in the UI as "X hour usage limit reached. It will reset in Y hours." — but the session does not actually wait for that reset; it just retries up to 5 times with exponential backoff.- Providers other than OpenCode Go (e.g. users bringing their own keys to fixed-window-quota providers) hit a 429 with no
metadata.limitNamein the body. The retry logic falls back to exponential backoff, exhausts the 5 attempts, and gives up — exactly when the user is most likely to need an automatic wait-and-resume.
In short: OpenCode already has the display half of quota handling (for Go) but not the wait-until-reset half, and the whole thing is only wired up for Go.
Proposed solution
- Generalise the existing quota classification.
packages/opencode/src/session/retry.tsalready has the primitives (LimitName = "5 hour" | "weekly" | "monthly",retry-afterparsing). Expose them through a per-provider config so any provider can declare its own reset schedule. - Read the schedule from
provider.<id>.quota, notprovider.options.quota. Theoptionsblock is pass-through to the AI SDK (timeout,chunkTimeout,setCacheKey,baseURL,apiKey,headers); quota / retry are OpenCode-internal behaviour, so they belong next tooptionsrather than inside it. - Actually wait for the next reset instead of giving up after 5 exponential attempts.
Config (extends the existing provider block; field names follow the camelCase convention used by baseURL, chunkTimeout, setCacheKey):
{
"provider": {
"<id>": {
"options": { "...": "..." },
"quota": {
"resetTimes": ["00:00", "05:00", "10:00", "15:00", "20:00"],
"weeklyResetDay": "monday",
"monthlyResetDay": 1
},
"retry": {
"maxRetriesPerWindow": 5,
"maxTotalRetries": 50
}
}
}
}
Behaviour when a quota error is hit:
- Classify error → 5h / weekly / monthly / transient (reuse the existing
LimitNametypes fromretry.ts). - 5h hit → next reset from
resetTimes, sleep, resume. - Weekly / monthly hit → surface a clear "X limit reached, resets on …" message; no auto-retry inside the session.
- After
maxRetriesPerWindowfailures inside one window, queue for the next window. - Hard ceiling
maxTotalRetriesprevents runaway sessions.
Backwards compatibility: providers without a quota block fall back to today's behaviour (exponential backoff, 5 attempts).
Related issues
- [FEATURE]:Automatically pause and resume tasks when rate limits have a known reset time #43126 — auto pause/resume on known reset time (closest match — semantic of "wait for the right reset" already exists, but no per-provider schema)
- Session retries fixed-window usage quota errors until reset #39790 / fix(session): stop retrying fixed-window usage quotas #39791 — fixed-window quota should not retry blindly
- [FEATURE]:Provider-Specific Rate Limiting #32423 — provider-specific rate limiting (RPM throttle, orthogonal)
- [FEATURE]:Add automatic retry with exponential backoff for LLM API timeouts #37412 —
ProviderConfig.options.maxRetries/...proposal (this extends that schema by addingprovider.quota/provider.retryat the top level) - fix(session): SessionRetry.policy() retries forever with no max attempt count #21960 / [Bug]: Session processor retries indefinitely with unbounded exponential backoff — no max retries or circuit breaker #17648 / Rate limit retries every ~1 second with no backoff or max limit (OpenAI provider) #30510 / LLM retry has no max attempts: stream errors cause infinite retry loop, UI stuck on Thinking #41848 — retry has no max attempt count
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
github-actions commentedon Aug 19, 2026
This issue might be a duplicate of or closely related to existing issues. Please check:
If these don't cover your use case, clarifying how this proposal differs from #43126 in particular would help maintainers evaluate it.
qzzpfssjlf commentedon Aug 22, 2026
Not a duplicate of #43126 — these are two layers of the same feature and I'd suggest keeping them linked rather than merged.
Concretely, #43324 only covers the "wait until reset" decision (classified via retry-after + metadata.limitName, then slept on), not the suspend/resume plumbing itself (that's #27511's scope). It also adds a per-provider config schema so any fixed-window provider can declare its own schedule, rather than hardcoding the Go subscription limits into retry.ts as today.
So: #43126 = UX thread, #27511 = mechanism, #43324 = retry-engine config + reset scheduling. Happy to fold the config/proposal section into #43126 if maintainers prefer a single thread — just want the reset-scheduling logic to land somewhere.
cannockwolf commentedon Sep 22, 2026
Hi I got astra to make me a plugin that is working perfectly atm here's an ai summary of what's created
I’ve implemented a local version of this behaviour as an OpenCode plugin and have now been using it successfully in normal use.
The basic flow is:
usage_limit_reached → read the provider’s reset timestamp → persist the recovery state → wait locally without model calls → resume the same session after reset.
A few things I’ve found from real-world testing:
I’ve been using this in production rather than just testing it synthetically, and automatic recovery has been reliable so far.
If this would be useful for the core implementation, I’m happy to provide more detai.