Description
SessionRetry.retryable() decides whether a provider error is worth another attempt from prose:
the message and the response body are matched against RETRYABLE_MESSAGE_PATTERNS, plus two
zen-specific markers (FreeUsageLimitError, GoUsageLimitError).
A provider can report an exhausted budget without any of that — no recognisable phrase, and no long
retry-after either. Those responses are classified as ordinary retryable errors, so the session
spends all five attempts on a limit that resets on a billing or daily window, and the real cause
never surfaces.
Concrete shapes this misses today:
- HTTP 402 — payment required. Nothing about the status is momentary.
- OpenAI billing codes in the body:
insufficient_quota, billing_hard_limit_reached,
billing_not_active, account_deactivated. These arrive as error.type / error.code on a 429
whose message ("You exceeded your current quota") matches the retryable patterns.
- Google
QuotaFailure with a per-day quota id, e.g.
GenerateRequestsPerDayPerProjectPerModel, on a 429 whose message is "Resource has been
exhausted" — which the existing exhausted branch treats as provider overload.
These are documented, stable identifiers, unlike the message text, which varies per provider and
locale.
Related but different: #39790 covers the zen fixed-window quotas and the long-retry-after case,
which #47339 and #47641 address. Neither reaches a structural budget signal that comes with a short
or absent retry-after and no zen marker.
Plugins
None
OpenCode version
dev (ea2d59d)
Steps to reproduce
- Use an OpenAI-compatible provider whose account has no remaining balance.
- Send a prompt.
- The provider answers 429 with
insufficient_quota (or 402), the message matches the retryable
patterns, and the session retries five times instead of reporting the exhausted budget.
Screenshot and/or share link
N/A
Operating System
Windows 11 (26200)
Description
SessionRetry.retryable()decides whether a provider error is worth another attempt from prose:the message and the response body are matched against
RETRYABLE_MESSAGE_PATTERNS, plus twozen-specific markers (
FreeUsageLimitError,GoUsageLimitError).A provider can report an exhausted budget without any of that — no recognisable phrase, and no long
retry-aftereither. Those responses are classified as ordinary retryable errors, so the sessionspends all five attempts on a limit that resets on a billing or daily window, and the real cause
never surfaces.
Concrete shapes this misses today:
insufficient_quota,billing_hard_limit_reached,billing_not_active,account_deactivated. These arrive aserror.type/error.codeon a 429whose message ("You exceeded your current quota") matches the retryable patterns.
QuotaFailurewith a per-day quota id, e.g.GenerateRequestsPerDayPerProjectPerModel, on a 429 whose message is "Resource has beenexhausted" — which the existing
exhaustedbranch treats as provider overload.These are documented, stable identifiers, unlike the message text, which varies per provider and
locale.
Related but different: #39790 covers the zen fixed-window quotas and the long-
retry-aftercase,which #47339 and #47641 address. Neither reaches a structural budget signal that comes with a short
or absent
retry-afterand no zen marker.Plugins
None
OpenCode version
dev (ea2d59d)
Steps to reproduce
insufficient_quota(or 402), the message matches the retryablepatterns, and the session retries five times instead of reporting the exhausted budget.
Screenshot and/or share link
N/A
Operating System
Windows 11 (26200)