anomalyco / anomalyco/opencode

[FEATURE]: Per-Provider Quota-Aware Auto-Retry with Configurable Reset Schedule

Open
#43,324 2 comments 0 reactions 1 assignee View on GitHub

@jlongster is already working on this.

Since Aug 19, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

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:

  • GoUsageLimitError already extracts metadata.limitName ("5 hour" | "weekly" | "monthly") and retry-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.limitName in 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
  1. Generalise the existing quota classification. packages/opencode/src/session/retry.ts already has the primitives (LimitName = "5 hour" | "weekly" | "monthly", retry-after parsing). Expose them through a per-provider config so any provider can declare its own reset schedule.
  2. Read the schedule from provider.<id>.quota, not provider.options.quota. The options block is pass-through to the AI SDK (timeout, chunkTimeout, setCacheKey, baseURL, apiKey, headers); quota / retry are OpenCode-internal behaviour, so they belong next to options rather than inside it.
  3. 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:

  1. Classify error → 5h / weekly / monthly / transient (reuse the existing LimitName types from retry.ts).
  2. 5h hit → next reset from resetTimes, sleep, resume.
  3. Weekly / monthly hit → surface a clear "X limit reached, resets on …" message; no auto-retry inside the session.
  4. After maxRetriesPerWindow failures inside one window, queue for the next window.
  5. Hard ceiling maxTotalRetries prevents runaway sessions.

Backwards compatibility: providers without a quota block fall back to today's behaviour (exponential backoff, 5 attempts).

Related issues
  • #43126 — auto pause/resume on known reset time (closest match — semantic of "wait for the right reset" already exists, but no per-provider schema)
  • #39790 / #39791 — fixed-window quota should not retry blindly
  • #32423 — provider-specific rate limiting (RPM throttle, orthogonal)
  • #37412 — ProviderConfig.options.maxRetries/... proposal (this extends that schema by adding provider.quota / provider.retry at the top level)
  • #21960 / #17648 / #30510 / #41848 — retry has no max attempt count

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.