Skip to content

Reset-credit success can leave the same account blocked by a local quota cooldown #3973

Description

@luvs01

Client or integration

OpenCodex dashboard

Area

Authentication and account pool

Summary

A successful manual reset-credit consume refreshes the account's WHAM usage, but does not reconcile that account's existing local quota cooldown. Subsequent native requests can therefore receive a local 429 before an upstream request is attempted, even after the operator has reset the account and refreshed its displayed usage.

The consume-success and routing paths were checked in v2.46.0 and v2.47.0; the relevant behavior remains on dev. This is a request to connect the manual reset outcome to the existing recovery contract, not to clear all account health after any successful HTTP response.

Reproduction

Observed intermittently on a long-running local proxy; the exact initial upstream error body was not retained, so this is not claimed as a deterministic live-account reproduction.

  1. Use native Codex models through the OpenCodex account pool, with an additional account selected and pinned.
  2. After that account has entered a reset-derived shared quota cooldown, consume a reset credit for that same account through the dashboard and refresh its displayed usage.
  3. Resume a native request. Observed local responses still report the selected account cooling down, with no upstream attempt recorded.
  4. A later, newly recorded cooldown is a confounder in the observed sequence. Verify the proposed fix with mocked reset and WHAM responses; do not consume another live credit for testing.

Evidence and limits

  • src/codex/auth-api.ts: the manual /api/codex-auth/reset-credits/consume success branch refreshes WHAM and returns remaining; it does not settle an existing quota-recovery claim.
  • src/codex/routing.ts: ordinary successful requests and quota-cache updates deliberately do not erase hard shared-scope cooldowns. Recovery is separately fenced by scope, lease and generation.
  • In local observations, repeated native requests were rejected in tens of milliseconds with no upstream attempts and the same reset-derived cooldown deadline. The public error exposed only the generic 429/retry-limit summary.
  • A later reset-derived cooldown was also observed after the reported manual reset time. The first failed physical send is not retained separately when an alternate-account retry succeeds, so those logs do not prove the original upstream error body or whether the reset itself failed. This issue does not claim that every post-reset 429 has this one cause.

Expected behavior

After a confirmed reset and a sufficiently fresh, complete usage observation, reconcile only the matching account's pre-existing shared reset-derived cooldown. Preserve explicit Retry-After, independent Spark/Reserve scopes, pause/pin settings, credential/identity changes and any newer quota failure.

The existing background recovery is related (#955), but a manual reset should not have to wait for its interval. The broader strict-quota/wait proposal #3738 is a separate scope.

Implementation constraints for a follow-up

  • Bind the candidate recovery to the authenticated account and a unique cooldown identity; generation numbers alone can be reused after deletion/recreation.
  • fetchPoolAccountQuota(..., true) may join an in-flight request started before the reset. Do not treat force=true as proof of a post-reset observation.
  • A durable replay or already_redeemed response is not proof that this invocation performed a new reset. Preserve idempotency and do not spend another credit to establish recovery.
  • Main and additional Pool identities have different proof contracts. Do not reuse a Pool credential proof for main, or clear another local alias merely because metadata appears similar.

Regression coverage can use mocked reset and quota responses; no real credit consumption is needed. It should cover successful recovery, pre-reset/stale/incomplete observations, a newer 429, identity replacement, deleted/recreated cooldown state, independent scopes and replayed operations.

auth-api.ts is a sponsored surface. Maintainer sponsorship and security review are requested for the follow-up implementation before review-ready status. No local account identifiers, raw responses or credentials are included here.

Version

Observed on 2.46.0. Relevant manual-reset and cooldown behavior also checked in 2.47.0 and dev dc5ee2f.

Operating system

Windows 11 Pro, version 10.0.26200 (build 26200).

Provider and model

OpenAI Codex account pool; gpt-6-astra and gpt-5.6-terra share the ordinary native quota scope.

Logs or error output

Redacted local record shape: status 429; errorCode rate_limit_exceeded; closeReason non_stream; attempts []; upstreamError describes the selected account's shared native quota cooling down with source reset-derived. The client shows exceeded retry limit, last status: 429 Too Many Requests.

Screenshots and supporting files

No raw account captures are attached. The source paths, version comparison and logging limitations are described above.

Redacted configuration

Codex account pool, fill-first selection, one additional account selected and pinned. Account identifiers and credentials are intentionally omitted.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. github-actions commented on Sep 8, 2026

    @github-actions
    Contributor

    Issue reopened

    The report now contains the information required by the automated check. Thanks for updating it.

  2. lidge-jun commented on Sep 8, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 61 / 80

    이 이슈는 대시보드에서 수동으로 reset credit를 쓴 뒤에도, 같은 Codex 풀 계정이 로컬에 남아 있는 reset-derived 쿼터 쿨다운 때문에 업스트림으로 한 번도 나가지 못한 채 로컬 429를 맞는 구멍입니다. 지금 dev HEAD 900567af3의 /api/codex-auth/reset-credits/consume 성공 분기를 보면, upstream consume이 reset / already_redeemed일 때 WHAM 사용량을 다시 읽고 remaining을 돌려줄 뿐이고, src/codex/routing.ts에 있는 공유 스코프 hard cooldown / probe lease / generation 정산 경로에는 손을 대지 않습니다. 그래서 카드에 보이는 사용량은 새로워져도, 라우팅은 예전 “이 계정은 아직 식는 중” 기록을 그대로 믿습니다.

    이건 “성공 HTTP면 계정 헬스 전부 지우자”는 요청이 아닙니다. 리포터도 분명히 말합니다. 이미 있는 복구 계약(settleCodexQuotaRecoveryProbe, scope·lease·generation)에 수동 reset이 확인된 그 계정·그 쿨다운 신원만 이어 달라는 것입니다. 배경 복구 스위프(#955 계열, claimDueCodexQuotaRecoveryProbes → WHAM 재조회)는 있지만 간격·선택 순서에 묶여 있어서, 운영자가 방금 크레딧을 쓴 직후에는 기다리지 않고도 같은 계정으로 다시 보낼 수 있어야 합니다. 보통 요청 성공이나 쿼터 캐시 갱신이 hard shared cooldown을 지우지 않는 현재 설계는 그대로 두는 게 맞습니다. 그 설계를 풀면 Retry-After·Spark/Reserve·핀/일시정지·새 429가 한꺼번에 풀릴 수 있습니다.

    지금 dev의 가까운 이웃과도 겹치지 않습니다. #3970은 auto-redeem 저널 직렬화이고, #3965는 수동 consume의 alias→canonical operationId 정산이며, #3738은 엄격 쿼터 대기/전환 제안입니다. 어느 것도 “수동 reset 성공 → 기존 reset-derived cooldown 정산”을 대신하지 않습니다. 재현은 라이브 크레딧을 또 쓰지 말고, mock reset + mock WHAM으로 회귀를 짜라는 제약도 HEAD와 잘 맞습니다. auth-api.ts는 sponsored surface라 후속 PR에 maintainer sponsorship + 보안 리뷰가 필요합니다.

    경로 src/codex/auth-api.ts · /api/codex-auth/reset-credits/consume 성공 분기 - reset/already_redeemed 후 WHAM refresh만 하고 settleCodexQuotaRecoveryProbe(또는 동등한 계정·스코프 바운드 정산)를 부르지 않습니다. 리포터가 지적한 핵심 구멍입니다.

    경로 src/codex/routing.ts · reset-derived hard cooldown - 일반 2xx/쿼터 캐시 갱신으로는 지우지 않도록 막혀 있습니다. 수동 reset이 이 울타리를 건너뛰지 않으면 로컬 429가 남습니다.

    경로 src/codex/auth-api.ts · 배경 cooldown recovery - probe 간격·선택에 묶인 best-effort라서, 방금 수동으로 쓴 직후 UX를 보장하지 않습니다. #955와 연결되지만 이 이슈의 대체재는 아닙니다.

    경로 fetchPoolAccountQuota(..., true) - in-flight 조인으로 reset 이전 관측이 올 수 있습니다. force=true만으로 post-reset 증거로 쓰면 안 됩니다. 리포트의 구현 제약이 맞습니다.

    경로 already_redeemed / durable replay - 새 reset 증명으로 쓰면 안 됩니다. idempotency를 지키면서도 “이번 호출이 실제로 리셋했다”와 “예전 성공을 재생했다”를 갈라야 합니다.

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

    • 후속 패치에서 정산 허용 증거: 신선한 완전 WHAM 스냅샷만인지, code=reset만인지, already_redeemed+신선한 usage까지 포함할지
    • main vs Pool 신원 증명 계약을 한 헬퍼로 묶을지, 경로를 분리할지
    • feat(codex): add quota-aware switching and resumable pool waits #3738 / 배경 probe와 역할 분담을 이슈에 한 줄로 고정할지
    • sponsorship 라벨을 이슈에 미리 달지, 구현 PR에만 달지

    너의 추천
    버그로 받아 두세요. 우선순위는 높은 편입니다. 운영자가 크레딧을 썼는데도 같은 계정이 로컬에서만 막히면 풀 UX가 깨집니다. 후속 PR은 (1) 확인된 reset 뒤에 같은 계정의 기존 shared reset-derived cooldown만 정산하고, (2) Retry-After·다른 스코프·새 429·신원 교체·삭제 후 재생성·재생 응답은 보존하며, (3) mock만으로 회귀를 넣고, (4) auth-api.ts sponsorship + 보안 리뷰를 거친 뒤 review-ready로 올리면 됩니다. 라이브 크레딧으로 재현 재확인은 하지 마세요.

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

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 working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions