Skip to content

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

Description

@qzzpfssjlf

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions