Skip to content

[Feature]: opt-in transient-5xx retry for key-auth providers on openai-chat adapter #2643

Description

@TooSpace

Area

Provider adapters

What are you trying to accomplish?

When a custom provider (e.g. an internal corporate LLM gateway) returns transient 5xx errors (502/503/504), the proxy should retry the request with exponential backoff before surfacing the error to the client. Currently this only works for the Google AI Studio adapter (fetchWithTransientRetry is gated behind route.provider.adapter === "google" in responses/core.ts), but it should be available to any key-auth provider via opt-in configuration.

What prevents this today?

The #1851 scope guard in src/server/responses/core.ts (lines 4597-4601 and 5059-5061) deliberately limits fetchWithTransientRetry to adapter === "google". Every other adapter keeps fetchWithResetRetry which only retries ECONNRESET/EPIPE connection resets, not HTTP 5xx responses. This means:

  • A 503 server_is_overloaded from a custom provider kills the turn immediately instead of waiting and retrying.
  • Users of providers that experience transient overload (e.g. shared corporate gateways) see sessions die mid-way instead of surviving a burst.

The retryOn429 config exists for 429 rate limiting but covers a different failure class. There is no mechanism to opt into transient-5xx retry for non-Google adapters.

What should OpenCodex do?

Introduce an opt-in provider config field (e.g. transientRetryOn5xx: { enabled: true, attempts?: number, baseDelayMs?: number, maxDelayMs?: number }) that allows any key-auth provider to enable fetchWithTransientRetry on the openai-chat adapter path. When enabled, the proxy should:

  1. Catch transient upstream statuses (500/502/503/504/520/521/522) on the initial request before streaming starts.
  2. Wait with exponential backoff (respecting Retry-After header), capped by a configurable max delay.
  3. Retry the identical request up to the configured attempts.
  4. Only surface the error to the client if all retries are exhausted.

The retry should be opt-in (disabled by default) so existing providers are not affected. The fetchWithTransientRetry implementation already handles Retry-After headers, heartbeats to keep stall watchdogs fed, and slow-attempt detection (returning a slow 502 as-is rather than retrying past the client timeout).

The same treatment should also apply to the chat-native.ts path (/v1/chat/completions), which currently uses fetchWithResetRetry for all non-Google adapters.

Example usage or interface

```json
// ~/.opencodex/config.json
{
"providers": {
"my-gateway": {
"adapter": "openai-chat",
"baseUrl": "http://gateway.internal/v1",
"authMode": "key",
"apiKey": "sk-...",
"transientRetryOn5xx": {
"enabled": true,
"attempts": 3,
"maxDelayMs": 5000
}
}
}
}
```

Before: A 503 from gateway.internal immediately kills the Codex turn with "Provider error 503".

After: The proxy waits ~400ms, retries; if another 503, waits ~800ms, retries again; only then surfaces the error. The turn survives transient overload.

Alternatives or workarounds

  1. Wrap the provider in a local retry proxy (e.g. nginx or a small Node script). This adds operational complexity and splits the timeout budget between two hops.
  2. Use a different adapter. Not applicable — openai-chat is the correct adapter for OpenAI-compatible endpoints.
  3. Rely on retryOn429. Inapplicable — 429 is rate-limiting, not server overload. Many corporate gateways return 503 under load rather than 429.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestproviderProvider adapters, OpenAI-compat presets, upstream API quirks

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions