Skip to content

oauth/xai: Retry-After is capped at 2000ms in postXaiToken, so a 429 with Retry-After: 60 retries after 2s #4045

Closed

Description

@lidge-jun

Where

src/oauth/xai.ts line 98 (retryDelay), on dev (blob f876b18):

return Math.min(2000,Math.max(j,seconds*1000));

Problem

Math.min(2000, ...) wraps the server-provided Retry-After value, so any delay the token endpoint asks for is truncated to 2 seconds. Measured on Node 24 with the function extracted verbatim:

retryDelay(2, "60", () => 0.5)  ->  2000

When auth.x.ai answers 429 with Retry-After: 60, postXaiToken sleeps 2s, hits 429 again, and burns all 3 attempts in ~4s instead of honoring the server. RFC 9110 §10.2.3 treats Retry-After as a value to honor, not a ceiling to clamp.

Fix

Apply the 2000ms cap to the jitter only, and honor Retry-After (with a generous absolute ceiling such as 60s to avoid pathological values):

if (seconds > 0) return Math.min(seconds * 1000, 60_000);
return Math.min(2000, j);

Repro

const random = () => 0.5;
retryDelay(1, "60", random); // expected 60000, got 2000

Found while porting this module into ima2-gen (devlog 260909_grok_native_oauth/001_opencodex_port_spec.md D1). Related: the HTTP-date form of Retry-After is also ignored (separate issue).

Activity

  1. added
    bugSomething isn't working
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    on Sep 8, 2026
  2. lidge-jun commented on Sep 8, 2026

    @lidge-jun
    OwnerAuthor

    리뷰 · 우선순위 62 / 80

    이 이슈는 Grok(xAI) OAuth 토큰 POST가 서버가 시킨 대기 시간을 지키지 못하는 버그입니다. 지금 dev HEAD cfff026d5의 src/oauth/xai.ts retryDelay(대략 98행)는 이렇게 끝납니다.

    return Math.min(2000, Math.max(j, seconds * 1000));

    서버가 Retry-After: 60을 줘도 Math.min(2000, …) 때문에 항상 최대 2초만 쉽니다. 이슈가 Node 24에서 함수만 빼서 측정한 값도 retryDelay(2, "60", () => 0.5) -> 2000입니다. postXaiToken은 429/5xx에서 최대 3번 재시도하므로, 실제로는 약 4초 안에 세 번을 다 태우고 실패합니다. RFC 9110 §10.2.3는 Retry-After를 존중하라고 말하고, 지금 코드는 존중이 아니라 상한으로 잘라 버립니다.

    왜 지금 큐에 올릴 만하냐면, dev는 패키지 2.49.0이고 Grok/xAI OAuth(XaiOAuthFlow / refreshXai)가 account-pool·로그인 경로에 이미 들어가 있습니다. 토큰 엔드포인트가 짧게라도 rate-limit을 걸면 로그인·리프레시가 “네트워크/토큰 요청 실패”로 보이기 쉽습니다. 같은 파일의 HTTP-date 파싱 구멍(#4046)과 abort 재시도 구멍(#4047)과 한 묶음으로 고치는 편이 싸습니다. types.ts/config.ts 분할과는 무관합니다.

    제안 수정은 명확합니다. 서버가 준 초 단위 값은 그대로(다만 60초 같은 절대 천장) 쓰고, 2000ms 캡은 지터 폴백에만 적용합니다.

    라인 98 · retryDelay - Math.min(2000, Math.max(j, seconds*1000))가 Retry-After를 2초로 자릅니다. 측정값과 코드가 일치합니다.

    경로 postXaiToken 재시도 루프 - 429마다 짧은 sleep만 하니 서버 의도(60초)와 클라이언트 행동(2초×N)이 어긋납니다. 사용자에게는 간헐적 Grok 로그인 실패로 보입니다.

    경로 테스트 - 이슈 본문의 retryDelay(1,"60") 단위 테스트만 있어도 회귀를 막을 수 있습니다. 지금은 공개 테스트가 안 보입니다.

    메인테이너의 판단이 필요한 지점

    너의 추천
    수락하세요. help wanted로 두되 우선은 중간 이상입니다. 가능하면 #4046과 같은 retryDelay PR에 넣고, 단위 테스트 한 줄을 같이 받으세요. close-don't-rebase 대상이 아닙니다.

    이 댓글은 grok-bot이 작성했습니다

  3. lidge-jun commented on Sep 9, 2026

    @lidge-jun
    OwnerAuthor

    Fixed on dev via #4087 at 4a08c75.

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

    account-poolOAuth, credentials, Codex pool, quota, failover, plansbugSomething isn't workingproviderProvider adapters, OpenAI-compat presets, upstream API quirks

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions