Skip to content

Honor Retry-After header in rate-limit retry policy and clarify terminal message - #839

Merged
TheGreatAxios merged 2 commits into
mainfrom
cl-7523-fix-rate-limit-retry-to-honor-retry-after-header-and-clarify
Sep 10, 2026
Merged

Honor Retry-After header in rate-limit retry policy and clarify terminal message#839
TheGreatAxios merged 2 commits into
mainfrom
cl-7523-fix-rate-limit-retry-to-honor-retry-after-header-and-clarify

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Summary

  • Retry policy respects retryAfterMs from provider adapters, pacing retries at min(retryAfterMs, MAX_BLIND_WAIT_MS) instead of fixed 500/1000ms backoff
  • Codex, Grok, and OpenAI Responses adapters implement extractRetryAfterMs to extract Retry-After / Retry-After-Ms headers from 429 responses
  • Terminal message for rate-limit errors changed from misleading "Rate limited — retrying…" to clear guidance: "Rate limited. Wait a moment and try again."

Verification

  • bun run check passes (lint, typecheck, build, projects-dir-guard)
  • Tests cover paced retry behavior, header extraction, and terminal message copy

Fixes CL-7523

@linear-code

linear-code Bot commented Sep 8, 2026

Copy link
Copy Markdown

CL-7523

@TheGreatAxios
TheGreatAxios force-pushed the cl-7523-fix-rate-limit-retry-to-honor-retry-after-header-and-clarify branch from 185c064 to 24bba00 Compare September 8, 2026 20:12

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Greybeard · Comment

Responses adapters extract Retry-After; Corbits policy paces remapped 429s instead of the 500/1000ms backoff.

Findings

  • src/agent/retry-policy.ts:49MAX_BLIND_WAIT_MS owns abort-vs-sleep for quota windows, not a delay cap. min(retryAfterMs, 30s) then retry waits 30s into the existing 45s xAI/Codex fixtures, retries early, and burns the attempt budget. Honor retryAfterMs on remapped retryable 429s; abort if the value is a hang.
  • src/agent/retry-policy.ts — hardcoded attempt >= 3 re-owns vendored MAX_ATTEMPTS. Ask defaultPolicy for abort-vs-retry; override only delayMs.

Notes

  • extractRetryAfterMs on Codex/Grok/OpenAI Responses is the right adapter boundary.
  • Terminal copy is the right surface for the "retrying" lie; that string only appears after abort.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critic · Comment

Retry policy honors Retry-After on retryable 429s (capped at 30s), Responses adapters extract the header, and terminal copy no longer claims an in-flight retry after abort.

Findings

  • src/agent/retry-policy.ts:59 — new path hardcodes attempt >= 3 instead of reusing vendored MAX_ATTEMPTS via defaultPolicy. Sequence: retryable 429 with retryAfterMs set, attempt === 3 aborts here; if vendor bumps the cap, this branch silently stays at 3. Override delayMs on a defaultPolicy retry (or share a constant) rather than re-capping.
  • src/provider/grok-responses-adapter.ts:255 / src/provider/openai-responses-adapter.ts:237extractRetryAfterMs is wired but only Codex asserts it (codex-responses-adapter.test.ts:67). Dropping the one-liner on Grok or OpenAI would not fail the suite.
  • src/provider/codex-responses-adapter.ts:643extractResponsesRetryAfterMs matches vendored Chat Completions (vendor/intx-inference/src/providers/openai.ts:1011): numeric retry-after / retry-after-ms only. HTTP-date Retry-After still yields undefined and the 500/1000ms fallback. Same as vendor; not a regression.

Notes

  • Remapped xAI/Codex 429 with retryAfterMs: 45_000 now waits MAX_BLIND_WAIT_MS (30s) rather than aborting or using 500ms. Documented by the updated tests; a header longer than 30s without quota body markers can still under-wait and burn the remaining attempts.
  • GATEWAY_OVERLOAD_USER_MESSAGE still says “retrying…” and can surface terminally. Out of this diff.

Capping the wait at the blind-wait ceiling retried while the
server was still closed. Attempt abort now follows defaultPolicy
so the cap cannot drift from the vendored maximum.
@TheGreatAxios
TheGreatAxios merged commit 48ea82c into main Sep 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant