Cortex supports advanced retry logic with exponential backoff, jitter, and Retry-After header support.
- Overview
- Quick Start
- Retry Policy Presets
- Custom Retry Configuration
- Configuration Levels
- Backoff Strategies
- Jitter Types
- Retryable Errors
- Database Schema
- Examples
The retry system automatically retries failed requests for transient errors (network issues, rate limits, temporary server errors) while respecting Retry-After headers and applying intelligent backoff strategies.
- Exponential Backoff: Progressively increase delay between retries
- Jitter: Randomize delays to prevent thundering herd
- Retry-After Support: Honor server-provided retry timing
- Error Classification: Automatic detection of retryable vs non-retryable errors
- Configurable Policies: Preset and custom retry configurations
- Per-Provider Settings: Different retry behavior for different providers
408- Request Timeout429- Too Many Requests (respects Retry-After header)500- Internal Server Error502- Bad Gateway503- Service Unavailable (respects Retry-After header)504- Gateway Timeout
oauth:
retry:
policy: "conservative" # Recommended default
providers:
anthropic:
oauth:
enabled: true
retry:
policy: "aggressive" # Override for specific providerproviders:
openai:
oauth:
enabled: true
retry:
policy: "custom"
max_attempts: 5
base_delay: 1s
max_delay: 30s
multiplier: 2.0
backoff_strategy: "exponential"
jitter_type: "full"
respect_retry_after: trueBest for production environments. Balances reliability with responsiveness.
retry:
policy: "conservative"Parameters:
- Max attempts: 3
- Base delay: 1s
- Max delay: 30s
- Multiplier: 2.0
- Jitter: Full
- Backoff: Exponential
Retry Timeline:
- Initial request fails
- Wait ~1s (0.5s - 1.5s with jitter)
- Retry 1 fails
- Wait ~2s (1s - 3s with jitter)
- Retry 2 fails
- Wait ~4s (2s - 6s with jitter)
- Retry 3 (final attempt)
Use for unreliable networks or critical requests where retries are preferred.
retry:
policy: "aggressive"Parameters:
- Max attempts: 5
- Base delay: 500ms
- Max delay: 30s
- Multiplier: 2.0
- Jitter: Full
- Backoff: Exponential
Retry Timeline:
- Initial request fails
- Wait ~500ms (250ms - 750ms with jitter)
- Retry 1 fails
- Wait ~1s (500ms - 1.5s with jitter)
- Retry 2 fails
- Wait ~2s (1s - 3s with jitter)
- Retry 3 fails
- Wait ~4s (2s - 6s with jitter)
- Retry 4 fails
- Wait ~8s (4s - 12s with jitter)
- Retry 5 (final attempt)
Disable retries completely. Use for testing or fast-fail scenarios.
retry:
policy: "none"Parameters:
- Max attempts: 1 (no retries)
For specific requirements, define your own retry policy.
| Option | Type | Description | Default |
|---|---|---|---|
policy |
string | Preset policy name | conservative |
max_attempts |
int | Maximum number of retry attempts | 3 |
base_delay |
duration | Initial delay between retries | 1s |
max_delay |
duration | Maximum delay between retries | 30s |
multiplier |
float | Backoff multiplier (≥ 1.0) | 2.0 |
backoff_strategy |
string | Backoff algorithm | exponential |
jitter_type |
string | Jitter algorithm | full |
respect_retry_after |
bool | Honor Retry-After headers | true |
retry:
policy: "custom"
max_attempts: 7
base_delay: 100ms
max_delay: 10s
multiplier: 1.5
backoff_strategy: "exponential"
jitter_type: "decorrelated"
respect_retry_after: trueretry:
policy: "custom"
max_attempts: 5
base_delay: 2s
max_delay: 10s
multiplier: 1.0
backoff_strategy: "linear"
jitter_type: "equal"
respect_retry_after: trueRetry configuration can be specified at multiple levels with the following precedence (highest to lowest):
- Provider OAuth Level - Most specific, applies only to that provider's OAuth token refresh
- Global OAuth Level - Applies to all providers using OAuth
- Default - Conservative policy if nothing is specified
# Global default for all OAuth providers
oauth:
retry:
policy: "conservative"
providers:
# Uses global conservative policy
anthropic:
oauth:
enabled: true
# Overrides with aggressive policy
openai:
oauth:
enabled: true
retry:
policy: "aggressive"
# Completely custom configuration
gemini:
oauth:
enabled: true
retry:
policy: "custom"
max_attempts: 5
base_delay: 1s
max_delay: 60s
multiplier: 2.0
backoff_strategy: "exponential"
jitter_type: "full"Delay doubles with each retry (modified by multiplier).
backoff_strategy: "exponential"
multiplier: 2.0 # Each retry waits 2x longer than previousDelay Sequence (base=1s, multiplier=2.0):
- Retry 1: 1s
- Retry 2: 2s
- Retry 3: 4s
- Retry 4: 8s
- Retry 5: 16s (capped at max_delay)
Delay increases by a constant amount.
backoff_strategy: "linear"
base_delay: 2s # Increment for each retryDelay Sequence (base=2s):
- Retry 1: 2s
- Retry 2: 4s
- Retry 3: 6s
- Retry 4: 8s
- Retry 5: 10s (capped at max_delay)
Same delay for all retries.
backoff_strategy: "constant"
base_delay: 3sDelay Sequence:
- All retries: 3s
Jitter randomizes delays to prevent synchronized retries across clients (thundering herd problem).
Random delay between 0 and calculated backoff.
jitter_type: "full"Example: If backoff = 4s, actual delay will be random between 0s and 4s.
Half the backoff plus random value up to half.
jitter_type: "equal"Example: If backoff = 4s, actual delay will be 2s + random(0, 2s) = between 2s and 4s.
Delay based on previous delay, not just the base backoff.
jitter_type: "decorrelated"More sophisticated algorithm that considers the previous actual delay.
Use exact calculated backoff with no randomization.
jitter_type: "none"Use only when: Testing or debugging, not recommended for production.
The following conditions trigger automatic retries:
- Network errors - Connection timeouts, DNS failures, etc.
- HTTP 408 - Request Timeout
- HTTP 429 - Too Many Requests (with Retry-After support)
- HTTP 500 - Internal Server Error
- HTTP 502 - Bad Gateway
- HTTP 503 - Service Unavailable (with Retry-After support)
- HTTP 504 - Gateway Timeout
The following errors are considered non-retryable:
- HTTP 4xx (except 408, 429) - Client errors
- HTTP 401 - Unauthorized
- HTTP 403 - Forbidden
- HTTP 404 - Not Found
- HTTP 400 - Bad Request
- Validation errors - Invalid input, parse errors
When a server returns HTTP 429 or 503 with a Retry-After header, Cortex will:
- Parse the header value (seconds or HTTP date)
- Use the specified delay instead of calculated backoff
- Continue with normal retry logic after the specified delay
HTTP/1.1 429 Too Many Requests
Retry-After: 10
# Cortex will wait 10 seconds before retrying
Retry configuration is stored in the database (migration 009) for persistence.
ALTER TABLE providers ADD COLUMN retry_policy TEXT DEFAULT 'conservative';
ALTER TABLE providers ADD COLUMN retry_max_attempts INTEGER DEFAULT 0;
ALTER TABLE providers ADD COLUMN retry_base_delay_ms INTEGER DEFAULT 0;
ALTER TABLE providers ADD COLUMN retry_max_delay_ms INTEGER DEFAULT 0;
ALTER TABLE providers ADD COLUMN retry_multiplier REAL DEFAULT 0.0;
ALTER TABLE providers ADD COLUMN retry_backoff_strategy TEXT DEFAULT '';
ALTER TABLE providers ADD COLUMN retry_jitter_type TEXT DEFAULT '';
ALTER TABLE providers ADD COLUMN retry_respect_retry_after BOOLEAN DEFAULT TRUE;ALTER TABLE provider_oauth ADD COLUMN retry_policy TEXT DEFAULT 'conservative';
-- (same columns as providers table)INSERT INTO server_config (key, value) VALUES ('retry.policy', 'conservative');
-- (additional retry config keys)Conservative defaults with aggressive retry for critical provider:
oauth:
retry:
policy: "conservative"
providers:
anthropic:
oauth:
enabled: true
# Uses global conservative policy
critical-service:
oauth:
enabled: true
retry:
policy: "aggressive" # More retries for critical serviceFast-fail for quick feedback:
providers:
all-providers:
oauth:
enabled: true
retry:
policy: "none" # No retries during developmentCustom configuration optimized for poor connectivity:
providers:
mobile-provider:
oauth:
enabled: true
retry:
policy: "custom"
max_attempts: 7
base_delay: 500ms
max_delay: 60s
multiplier: 1.5
backoff_strategy: "exponential"
jitter_type: "decorrelated"
respect_retry_after: trueRespect rate limits with longer delays:
providers:
rate-limited-api:
oauth:
enabled: true
retry:
policy: "custom"
max_attempts: 3
base_delay: 5s
max_delay: 120s
multiplier: 3.0
backoff_strategy: "exponential"
jitter_type: "full"
respect_retry_after: true- Use Conservative as Default: The conservative preset works well for most production scenarios
- Enable Retry-After: Always set
respect_retry_after: trueto honor server guidance - Add Jitter in Production: Use
fullorequaljitter to prevent thundering herd - Don't Over-Retry: More retries != better; 3-5 attempts is usually sufficient
- Monitor Retry Metrics: Track retry rates to identify problematic providers
- Test Both Paths: Test both success and retry scenarios
- Consider Costs: Each retry consumes resources; balance reliability with efficiency
- Check policy is not set to
none - Verify error is retryable (check status code)
- Ensure max_attempts > 1
- Check logs for retry attempts
- Reduce
max_attempts - Increase
base_delayandmax_delay - Consider using
nonepolicy for non-critical operations
- Reduce
max_delay - Use linear or constant backoff instead of exponential
- Reduce
multiplier
- Ensure
respect_retry_after: true - Increase
base_delay - Use larger
multiplier(3.0 or higher) - Consider reducing concurrent requests
Existing configurations without retry settings will automatically use the conservative policy. No migration is required, but you can opt-in to custom policies by adding the retry configuration.
Migration 009 automatically adds retry configuration columns with conservative defaults. Existing OAuth providers will use conservative retry policy unless explicitly configured otherwise.