Repository navigation
feat: configure API retry backoff (#370) - #1095
Conversation
Add OpenClaude-branded retry controls for retryable API failures. - Replace the old CLAUDE_CODE_MAX_RETRIES config with OPENCLAUDE_MAX_RETRIES - Allow OPENCLAUDE_MAX_RETRIES=0 to disable retries after the initial request - Cap retry attempts at 100 and invalid values fall back to the default of 10 - Add OPENCLAUDE_RETRY_DELAY_MS to configure the exponential backoff base for APIs that omit Retry-After - Keep Retry-After precedence over configured retry delay - Document both settings in .env.example and advanced setup docs - Add focused retry configuration tests for defaults, invalid values, caps, zero retries, configured delay, and Retry-After precedence Validation: - bun test src/services/api/withRetry.test.ts - bun run build
techbrewboss
left a comment
There was a problem hiding this comment.
Thanks for the focused retry-config slice. The new OPENCLAUDE_RETRY_DELAY_MS path behaves as described in the branch tests, and bun run build passes locally.
I found one compatibility issue with the env var rename that should be handled before merge.
Validation I ran locally:
bun run buildpassedbun test src/services/api/withRetry.test.tscurrently fails on the known isolated-import issue:Cannot find module './commands/fork/index.js' from 'src/commands.ts'. After that first import failure, the remaining retry assertions in this file pass.
| return parseInt(process.env.CLAUDE_CODE_MAX_RETRIES, 10) | ||
| } | ||
| return DEFAULT_MAX_RETRIES | ||
| return validateRetryAttemptsEnvVar(process.env.OPENCLAUDE_MAX_RETRIES) |
There was a problem hiding this comment.
This drops support for the previously documented CLAUDE_CODE_MAX_RETRIES setting without any fallback. Existing users who already set CLAUDE_CODE_MAX_RETRIES=0 or tuned it down for CI will silently go back to the default 10 retries after upgrading, which is a behavioral regression in the same retry path this PR is changing. Can we keep a compatibility fallback when OPENCLAUDE_MAX_RETRIES is unset, preferably with the new env var taking precedence and maybe a debug/deprecation message for the legacy name?
Add compatibility fallback from CLAUDE_CODE_MAX_RETRIES when OPENCLAUDE_MAX_RETRIES is unset. Document the deprecated fallback and cover precedence behavior in retry configuration tests.
techbrewboss
left a comment
There was a problem hiding this comment.
Re-reviewed current head f41c1972. The compatibility issue I raised earlier is fixed: OPENCLAUDE_MAX_RETRIES takes precedence, and CLAUDE_CODE_MAX_RETRIES is still honored as a deprecated fallback when the new env var is unset. The docs and tests cover that behavior.
Validation I ran locally:
CLAUDE_CODE_MAX_RETRIES=0with noOPENCLAUDE_MAX_RETRIESreturns0fromgetDefaultMaxRetries()bun test src/services/api/withRetry.test.tspasses: 28/28git diff upstream/main...HEAD --checkpassesbun run buildpasses
I do not see a remaining blocker.
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Targeted review of current head f41c197, focused on the retry configuration behavior for #370.
Verdict: Approve-ready
What I checked:
OPENCLAUDE_MAX_RETRIESis the new primary setting.CLAUDE_CODE_MAX_RETRIESremains a deprecated fallback when the OpenClaude setting is unset, so existing users do not silently lose their retry config.OPENCLAUDE_MAX_RETRIES=0correctly disables retries after the initial request.Retry-Afterremains authoritative over configured fallback delay.- Ran
bun test ./src/services/api/withRetry.test.ts: 28/28 passing.
I do not see a blocker on current head.
BlockersNone. Non-BlockingNone. Looks Good
Verdict: Approve — clean retry backoff feature. |
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Clean retry backoff feature.
gnanam1990
left a comment
There was a problem hiding this comment.
Approving — clean, well-scoped retry-config slice. 🎉
I verified the safety properties: bounded (100-retry / 60s caps), invalid values fall back to defaults, OPENCLAUDE_MAX_RETRIES=0 correctly disables retries, server-provided Retry-After stays authoritative over the configured fallback, and legacy CLAUDE_CODE_MAX_RETRIES is honored as a documented deprecated fallback so existing users don't silently lose config. No new network surface in third-party paths, no red flags, and the tests cover the matrix and pass on current head. Maps cleanly to #370. Thanks!
2d01cbe
* feat: configure API retry backoff Add OpenClaude-branded retry controls for retryable API failures. - Replace the old CLAUDE_CODE_MAX_RETRIES config with OPENCLAUDE_MAX_RETRIES - Allow OPENCLAUDE_MAX_RETRIES=0 to disable retries after the initial request - Cap retry attempts at 100 and invalid values fall back to the default of 10 - Add OPENCLAUDE_RETRY_DELAY_MS to configure the exponential backoff base for APIs that omit Retry-After - Keep Retry-After precedence over configured retry delay - Document both settings in .env.example and advanced setup docs - Add focused retry configuration tests for defaults, invalid values, caps, zero retries, configured delay, and Retry-After precedence Validation: - bun test src/services/api/withRetry.test.ts - bun run build * Honor legacy max retries env var Add compatibility fallback from CLAUDE_CODE_MAX_RETRIES when OPENCLAUDE_MAX_RETRIES is unset. Document the deprecated fallback and cover precedence behavior in retry configuration tests. --------- Co-authored-by: JATMN <12479882+jatmn@users.noreply.github.com>
- fix(autocompact): retry circuit breaker after cooldown (Twigpine#1375) - fix(provider): require API key input when adding OpenGateway (Twigpine#1384) - fix(provider): allow remote Ollama without OPENAI_API_KEY (Twigpine#952) - fix(codex-stream): recover tool args delivered only via done events (Twigpine#1262) - fix: route MiniMax compacting through Anthropic-compatible API (Twigpine#1154) - fix(thinking): disable thinking for unsupported Ollama models (Twigpine#1376) - feat(agents): set active session agent from agents menu (Twigpine#1349) - fix(repl): show permission prompts while draft input is present (Twigpine#1393) - fix(model): include profile models in descriptor picker (Twigpine#1361) - Improve warning notice formatting (Twigpine#1415) - fix(codex): allow credential storage fallback (Twigpine#1347) - fix(attribution): make git attribution opt-in by default (Twigpine#1335) - fix(agent): allow custom model overrides (Twigpine#1337) - feat(query): robust multi-lingual and structural continuation nudge (Twigpine#1280) - fix(watchers): debounce skills and settings reload bursts (Twigpine#1370) - feat: configure API retry backoff (Twigpine#370) (Twigpine#1095) - chore(main): release 0.15.0 (Twigpine#1325) - ci: retrigger CodeQL after action download outage (Twigpine#1374) - Fix launcher heap setup for long sessions (Twigpine#1242)
Backport of upstream d02c10b 'feat: configure API retry backoff (Twigpine#370) (Twigpine#1095)' with fork-rebranded env var names (OPENCLAUDE_* → OPENCC_*). The previous tier 2 sync (939802d) dropped the 9 'retry configuration' tests in withRetry.test.ts because the upstream code added OPENCLAUDE_MAX_RETRIES / OPENCLAUDE_RETRY_DELAY_MS env var support that the fork intentionally didn't port. This commit ports the feature properly under the fork's preferred OPENCC_ naming. What OPENCC_MAX_RETRIES does: - Replaces legacy CLAUDE_CODE_MAX_RETRIES as the primary config - Allow 0 to disable retries after the initial request - Cap at 100, fall back to default (10) for invalid values - CLAUDE_CODE_MAX_RETRIES is still honored as a deprecated fallback with a logForDebugging deprecation notice (per fork policy: keep CLAUDE_CODE_* env vars as user-facing API) What OPENCC_RETRY_DELAY_MS does: - Configures base retry delay (ms) for APIs that don't send Retry-After - Capped at 60000, falls back to default (500) for invalid values - Retry-After header takes precedence over the configured delay Files changed (4): - src/services/api/withRetry.ts: add validateRetryAttemptsEnvVar helper, import validateBoundedIntEnvVar, refactor getDefaultMaxRetries to read OPENCC_MAX_RETRIES first then fall back to CLAUDE_CODE_MAX_RETRIES, add getDefaultRetryDelayMs() function, wire baseDelayMs into getRetryDelay exponential backoff - src/services/api/withRetry.test.ts: add 3 new env keys to envKeys array, re-add the 9 'retry configuration' tests with OPENCC_ names, rename existing OPENCLAUDE_RETRY_DELAY_MS → OPENCC_RETRY_DELAY_MS in the 'OpenAI-compatible retry classification' block (8 occurrences) - .env.example: add OPENCC_MAX_RETRIES + OPENCC_RETRY_DELAY_MS documentation in the OPTIONAL TUNING section - docs/advanced-setup.md: add 2 new env var table rows Verification: typecheck: 0 errors build: Built v0.16.1 → dist/cli.mjs bun test: 2544 pass / 0 fail / 34 skip (full suite, +12 vs 939802d) naming: no OPENCLAUDE_(MAX_RETRIES|RETRY_DELAY) leak
Backport of upstream d02c10b 'feat: configure API retry backoff (Twigpine#370) (Twigpine#1095)' with fork-rebranded env var names (OPENCLAUDE_* → OPENCC_*). The previous tier 2 sync (939802d) dropped the 9 'retry configuration' tests in withRetry.test.ts because the upstream code added OPENCLAUDE_MAX_RETRIES / OPENCLAUDE_RETRY_DELAY_MS env var support that the fork intentionally didn't port. This commit ports the feature properly under the fork's preferred OPENCC_ naming. What OPENCC_MAX_RETRIES does: - Replaces legacy CLAUDE_CODE_MAX_RETRIES as the primary config - Allow 0 to disable retries after the initial request - Cap at 100, fall back to default (10) for invalid values - CLAUDE_CODE_MAX_RETRIES is still honored as a deprecated fallback with a logForDebugging deprecation notice (per fork policy: keep CLAUDE_CODE_* env vars as user-facing API) What OPENCC_RETRY_DELAY_MS does: - Configures base retry delay (ms) for APIs that don't send Retry-After - Capped at 60000, falls back to default (500) for invalid values - Retry-After header takes precedence over the configured delay Files changed (4): - src/services/api/withRetry.ts: add validateRetryAttemptsEnvVar helper, import validateBoundedIntEnvVar, refactor getDefaultMaxRetries to read OPENCC_MAX_RETRIES first then fall back to CLAUDE_CODE_MAX_RETRIES, add getDefaultRetryDelayMs() function, wire baseDelayMs into getRetryDelay exponential backoff - src/services/api/withRetry.test.ts: add 3 new env keys to envKeys array, re-add the 9 'retry configuration' tests with OPENCC_ names, rename existing OPENCLAUDE_RETRY_DELAY_MS → OPENCC_RETRY_DELAY_MS in the 'OpenAI-compatible retry classification' block (8 occurrences) - .env.example: add OPENCC_MAX_RETRIES + OPENCC_RETRY_DELAY_MS documentation in the OPTIONAL TUNING section - docs/advanced-setup.md: add 2 new env var table rows Verification: typecheck: 0 errors build: Built v0.16.1 → dist/cli.mjs bun test: 2544 pass / 0 fail / 34 skip (full suite, +12 vs 939802d) naming: no OPENCLAUDE_(MAX_RETRIES|RETRY_DELAY) leak
Summary
OPENCLAUDE_MAX_RETRIESOPENCLAUDE_RETRY_DELAY_MSCLAUDE_CODE_MAX_RETRIESconfiguration name in the retry path.OPENCLAUDE_MAX_RETRIES=0to disable retries after the initial request.Retry-Afterheaders authoritative when they are present..env.exampleanddocs/advanced-setup.md.Retry-Afterprecedence.Why
Fixes #370.
Some OpenAI-compatible providers, including providers with per-second rate limits, can return transient 429 responses without a useful
Retry-Afterheader. Before this change, OpenClaude had retry behavior, but users could not tune the fallback backoff delay for providers that omitRetry-After, and the exposed retry-count env var still used old Claude Code branding.This gives users a clear OpenClaude configuration surface for retry count and fallback retry delay while preserving the existing behavior by default.
User Impact
Users can now tune retry behavior without changing code:
OPENCLAUDE_MAX_RETRIES=0disables retries after the initial request.When an API response includes
Retry-After, OpenClaude still honors that server-provided delay instead of the configured fallback delay.Provider Paths
This affects the shared API retry path used for retryable API failures, including OpenAI-compatible providers. The most relevant path for issue #370 is providers that return retryable 429s without
Retry-After.Validation
bun installbun run builddist/cli.mjsdist/sdk.mjspackage.json, but exited successfullybun run smokenode dist/cli.mjs --versionreturned0.9.2 (OpenClaude)bun test src/services/api/withRetry.test.tsNotes
main.