You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Rate limit retries every ~1 second with no backoff or max limit (OpenAI provider) #30510
When hitting rate limits (429) with OpenAI providers (e.g., ChatGPT 5.5), opencode retries approximately every second indefinitely, with no effective exponential backoff and no maximum retry limit.
Root Cause
Three compounding issues:
OpenAI SDK internal retries not disabled
opencode sets maxRetries: input.retries ?? 0 on the AI SDK's streamText() call, which disables retries at the AI SDK layer. However, the underlying OpenAI SDK (openai npm package) has its own separate retry mechanism with DEFAULT_MAX_RETRIES = 2, which is never overridden. This means every call to the OpenAI provider gets retried up to 2 times internally with ~0.5-1s delays before the error even reaches opencode's session retry logic.
The OpenAI SDK's RetryHelper always retries 429s, creating a hidden retry loop invisible to opencode's backoff calculation.
Off-by-one in backoff delay calculation
The session retry delay formula in session/retry.ts:
RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1)
// = 2000 * 2^(attempt - 1)
Effect's Schedule.InputMetadata.attempt is 0-indexed, so on the first retry attempt = 0, yielding:
2000 * 2^(0-1) = 2000 * 0.5 = 1000ms
This halves the intended initial delay from 2 seconds to 1 second.
No maximum retry limit
The session retry policy (retry.ts → policy()) retries indefinitely as long as retryable() returns truthy. For 429 rate limit errors, it always does. The backoff caps at 30 seconds (RETRY_MAX_DELAY_NO_HEADERS) but never stops retrying.
Observed Behavior
With a persistent rate limit from ChatGPT 5.5:
OpenAI SDK retries 2x internally (~0.5-1s apart)
Error propagates to session retry → waits ~1s (off-by-one)
Calls OpenAI SDK again → 2 more internal retries
Repeat forever
This creates the appearance of ~1 second retries with no backoff.
Expected Behavior
OpenAI SDK's internal retries should be disabled (set maxRetries: 0 on the provider SDK) so opencode's own backoff logic has full control
Initial retry delay should be 2 seconds, not 1 second (fix off-by-one: use attempt instead of attempt - 1, or adjust for 0-indexing)
Session retry should have a configurable maximum retry limit (e.g., 5-10 attempts) before giving up and surfacing the error to the user
Workaround
Press Escape / Ctrl+C to abort the session when stuck in a retry loop.
Affected Code
packages/opencode/src/session/retry.ts — delay formula and no max limit
packages/opencode/src/provider/provider.ts — OpenAI SDK maxRetries not set to 0
packages/opencode/src/session/llm.ts — maxRetries: input.retries ?? 0 only affects AI SDK layer
If your issue is distinct (e.g., the specific OpenAI SDK internal retry interaction described here), please clarify what makes it different from the above.
I hit this in production-like conditions and got to the exact mechanism.
Root cause: on quota-class 429s, Azure OpenAI returns both retry-after: 30 and retry-after-ms: 0. delay() in packages/opencode/src/session/retry.ts gives retry-after-ms priority — and "0" is truthy, parseFloat("0") === 0 passes the NaN guard, so it returns a 0ms delay on every attempt. Combined with the session retry policy having no max attempt count (#21960, #17648), the loop runs at network round-trip speed: I measured ~5 sustained requests/second for ~30 minutes (thousands of requests, zero tokens served, no error surfaced).
Repro: point opencode at a local mock that always answers 429 with both headers above — it hammers at RTT speed. Remove retry-after-ms from the mock and it politely retries every 30s. The diff between those two runs is the bug. (Most mocks only send retry-after, which is why this hides.)
Deterministic check: calling delay(n, error) with responseHeaders: { "retry-after": "30", "retry-after-ms": "0" } returns 0 for any n.
Proposed fix (happy to PR): treat non-positive hints as "no usable hint" — add && parsedMs > 0 / && parsedSeconds > 0 so a zero/negative value falls through (ms → seconds → exponential backoff). With Azure's real headers the delay becomes 30s. A separate PR could add a max attempt count to SessionRetry.policy per #21960. Note: one existing test ("policy updates retry status and increments attempts") uses retry-after-ms: "0" as a fixture and implicitly depends on the 0ms behaviour — it would need a small fixture bump.
Activity
github-actions commentedon Jun 3, 2026
This issue might be a duplicate of existing issues. Please check:
If your issue is distinct (e.g., the specific OpenAI SDK internal retry interaction described here), please clarify what makes it different from the above.
Jomy2323 commentedon Jul 10, 2026
I hit this in production-like conditions and got to the exact mechanism.
Root cause: on quota-class 429s, Azure OpenAI returns both retry-after: 30 and retry-after-ms: 0. delay() in packages/opencode/src/session/retry.ts gives retry-after-ms priority — and "0" is truthy, parseFloat("0") === 0 passes the NaN guard, so it returns a 0ms delay on every attempt. Combined with the session retry policy having no max attempt count (#21960, #17648), the loop runs at network round-trip speed: I measured ~5 sustained requests/second for ~30 minutes (thousands of requests, zero tokens served, no error surfaced).
Repro: point opencode at a local mock that always answers 429 with both headers above — it hammers at RTT speed. Remove retry-after-ms from the mock and it politely retries every 30s. The diff between those two runs is the bug. (Most mocks only send retry-after, which is why this hides.)
Deterministic check: calling delay(n, error) with responseHeaders: { "retry-after": "30", "retry-after-ms": "0" } returns 0 for any n.
Proposed fix (happy to PR): treat non-positive hints as "no usable hint" — add && parsedMs > 0 / && parsedSeconds > 0 so a zero/negative value falls through (ms → seconds → exponential backoff). With Azure's real headers the delay becomes 30s. A separate PR could add a max attempt count to SessionRetry.policy per #21960. Note: one existing test ("policy updates retry status and increments attempts") uses retry-after-ms: "0" as a fixture and implicitly depends on the 0ms behaviour — it would need a small fixture bump.
github-actions commentedon Sep 9, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.