Skip to content

[Bug] Main-account hard lock outlives an early reset redeemed outside opencodex until the old predicted reset #7312

Closed

Description

@fwkyle

Client or integration

Codex CLI

Area

Authentication and account pool

Summary

When the main account's usage is reset early, outside opencodex (a reset credit redeemed somewhere other than ocx account reset-credits), the main-account hard lock keeps refusing every native request locally until the previously predicted reset time, which can be days away. Nothing in the default configuration ever re-reads usage in that state, so the lock only clears when someone opens the dashboard or runs ocx account refresh openai.

Why it cannot notice the early reset:

  1. While codexMainAccountHardLock blocks, requests are refused locally (5–140 ms, failureStage: headers-only), so no upstream response can carry fresh usage.
  2. runMainAccountHardLockRecovery() returns before probing whenever the blocking window's reset is still in the future (src/codex/auth-api/pool-mode-gate.ts:104 on dev: "A predicted reset is a scheduling hint, never proof that the hard lock can be lifted."). This wait is intentional (fix(codex): pace quota recovery queries #6179) and covered by known reset waits locally, then verifies recovery instead of unlocking by time in tests/codex-integration/main-account-hard-lock-recovery.test.ts.
  3. The only other periodic reader, src/quota/reset-poller.ts, does nothing unless quotaResetNotify is enabled, and it is disabled by default.
  4. Redeeming through opencodex is fine: consumeResetCredits() calls refreshAfterManualReset() and publishes fresh usage. The gap is a reset that happens anywhere else (Codex App, web, or an upstream-side reset), which the "wait for the predicted reset" assumption does not cover.

So the design assumption "usage cannot drop before the known reset" no longer holds once reset credits exist, and the lock can outlive the real block by up to a weekly window.

Expected: an early reset should be noticed within a bounded time without user action. Some options, in order of how little they change:

  • While blocked with a future reset, still allow the recovery sweep to read WHAM usage on a slow, capped cadence (for example every 30–60 minutes, reusing the existing nextQuotaQueryDelay / Retry-After pacing), instead of not at all until the reset.
  • Or treat a non-zero resetCredits count on the main account as "an early reset is possible" and only then enable that slow probe.
  • At minimum, surface it in the 429 message: the current text suggests waiting or disabling the lock, but not that ocx account refresh openai clears a stale block.

Reproduction

  1. Use the main (native Codex login) account until the hard lock blocks (here, observed message: Codex main account is blocked by the main-account quota policy (5h ≥ 90%, weekly ≥ 98%)).
  2. Redeem a reset credit outside opencodex (here, not via ocx account reset-credits; the ocx service log has no reset-credit call).
  3. Send Codex CLI requests through opencodex: every native OpenAI model returns local 429 rate_limit_exceeded (observed for gpt-6-luna, gpt-5.6-luna, gpt-6.1-sol, gpt-6-astra, 59 of 59 recent native requests), while non-OpenAI providers keep returning 200.
  4. Run ocx account refresh openai: it reports weekly 3%, and the next Codex CLI request returns 200 immediately.

I could not recover the exact pre-refresh cached quota tuple (the cache file was overwritten by the refresh), so which window was blocking and its stored reset time are not shown here.

Version

opencodex 2.83.0 (npm @bitkyc08/opencodex); the same code path is unchanged on dev as of 2026-10-17. Codex CLI 0.163.0.

Operating system

macOS (Darwin 25.5.0, arm64)

Provider and model

openai (native Codex main account, Pro plan) — gpt-6.1-sol, gpt-6-luna, gpt-5.6-luna, gpt-6-astra

Logs or error output

status: 429, durationMs: 14, errorCode: rate_limit_exceeded, failureStage: headers-only
upstreamError: Codex main account is blocked by the main-account quota policy (5h ≥ 90%, weekly ≥ 98%). Choose another account, wait for quota to reset, or disable codexMainAccountHardLock in Settings.
routeDecision.selected: { provider: openai, model: gpt-6-luna, reason: native-family }

After ocx account refresh openai:

main  pro  weekly 3%  resets 2026-10-24T22:01:09.000Z

Checks

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

Activity

  1. added
    bugSomething isn't working
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    serviceService lifecycle (WinSW/launchd/scheduler)
    on Oct 18, 2026
  2. Ingwannu commented on Oct 18, 2026

    @Ingwannu
    Owner

    Confirmed the scheduling gap statically on dev 8640c2f30edb: runMainAccountHardLockRecovery has both a pre-acquisition and a pre-query future-reset return. Requests refused by the lock cannot refresh it through inference headers. The missing pre-refresh tuple limits attribution of the historical 59 requests to one exact blocking window, but does not remove this deterministic early-reset case.

    The existing hard-lock diagnostic guide already lists ocx account refresh openai as a metadata refresh: fresh below-threshold usage or validated window absence releases the lock. It is not an unconditional unlock, and disabling the policy is not required when fresh evidence proves recovery.

    A runtime fix changes the deliberate known-reset scheduling contract from #6179, so it needs an owner decision on a bounded metadata-only recheck cadence. Do not unlock by elapsed time, issue inference, spend/redeem credits, or rely on a cached positive reset-credit count as the only permission to discover an external reset. Reuse the current single-flight/profile lease, identity+credential/config fencing and shared Retry-After/query backoff. Test a future known reset whose injected metadata drops early, repeated sweeps without duplicate reads, failed/incomplete observations retaining the lock, pauses/reauth, credential replacement and stale completion. A successful later observation must clear only the matching physical account. Keep unknown/no-reset recovery and credit-renewal behavior as controls.

    Leaving this bug open for that scheduling decision; #7290 concerns stored-pool credit evidence and is not a duplicate. No real usage query, reset redemption or local configuration change was executed in this triage.

  3. added
    needs-design설계 필요. Implementation waits on a written design.
    on Oct 18, 2026
  4. lidge-jun commented on Oct 19, 2026

    @lidge-jun
    Owner

    Fixed on dev by #7393 (squash d3dafd3): a predicted reset now arms a capped, metadata-only WHAM recheck schedule (5/10/20/40, then at most every 60 min; no inference, no reset redemption, paused/needs-reauth main excluded), so an early reset redeemed outside opencodex is learned from verified metadata. A predicted reset alone still never lifts the lock. It ships in the next release (2.90.0).

  5. github-actions commented on Oct 19, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  6. JHulsinger commented on Oct 21, 2026

    @JHulsinger

    The 2.90.0 fix still leaves users locked out for 5–60 minutes after an external reset. Suggest checking live usage before refusing.

    What happened (2.84.0)

    • Main account hit shortPercent: 90, which trips the default hard lock. I redeemed a reset outside opencodex.
    • opencodex kept returning local 429s (5–43 ms, failureStage: "headers-only"). The same request sent directly to chatgpt.com/backend-api/codex succeeded.
    • After upgrading to 2.90.0, it still said "Try again at 1:52 PM", the old reset time, until the first scheduled recheck.

    Suggestions

    1. Before the hard lock refuses a request, read WHAM usage once. Cap it at one read per 30–60 s per account.
    2. Feed the usage that UsageRelayController.rewriteJson already parses into the quota store the hard lock reads.
    3. Say in the 429 that opencodex refused the request, not OpenAI.
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 workingneeds-design설계 필요. Implementation waits on a written design.serviceService lifecycle (WinSW/launchd/scheduler)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions