Skip to content

Main-account hard lock never releases when the blocking 5h window stops being observed #6244

Closed

Description

@nowhere1975

Client or integration

OpenCodex dashboard

Area

Authentication and account pool

Summary

The 98% main-account hard lock (codexMainAccountHardLock) can never release when the observation that blocks it is a 5h (shortPercent) window that no longer appears in any new observation. The stale evidence is carried forward forever, so mainAccountHardLock.state stays blocked, the main account is permanently excluded from the pool, and the dashboard shows a permanent 98% protection badge for an account whose live weekly usage is 0%.

Observed for 19 days on this machine: every restart, every ocx account refresh openai, and the built-in runMainAccountHardLockRecovery() sweep produced the same blocked state.

Why it cannot self-heal:

  1. getMainAccountHardLockStatus() (src/codex/main-account-hard-lock.ts:67-74) blocks while any governing window reads >= 98, and documents the release rule: "A predicted reset is not evidence of recovery either way: only a fresh lower reading releases." Because the blocking reset is in the past, resetAt is omitted from the status (line 73), so the dashboard renders a badge with no reset countdown.
  2. assignCarriedShort() (src/codex/quota.ts:257-269) deliberately refuses to expire blocking evidence: preserveBlockingEvidence = policyEvidence && shortPercent >= 98 && shortPercent <= 100, and only the !preserveBlockingEvidence branch consults shortResetHasElapsed(). The comment states "a reset clock cannot retract blocking hard-lock evidence".
  3. This account's new observations never contain a 5h window: quotas.__main__ carries only weeklyPercent, and history.accounts has no __main__ entry at all. So no fresh shortPercent can ever overwrite the 2026-09-11 reading, and nothing else is permitted to retract it.
  4. runMainAccountHardLockRecovery() (src/codex/auth-api/pool-mode-gate.ts:79-150) behaves as designed — it passes the "reset is already in the past" guard and keeps probing with backoff — but can only ever receive the same weekly-only snapshot. It retried for 19 days without effect.
  5. Policy evidence has no TTL by design ("Policy evidence deliberately outlives the legacy six-hour rotation-cache TTL", src/codex/quota.ts:693-697), so the stale tuple survives every restart.

ocx doctor additionally reports the usage probe failing, which cannot help either: https://chatgpt.com/backend-api/wham/usage -> error=timeout, 8010ms, authenticated, with PROBE_TIMEOUT_MS = 8000 (src/cli/doctor.ts:304). The same URL through the same outbound proxy returns 401 in 0.83s (curl -x http://127.0.0.1:7890 https://chatgpt.com/backend-api/wham/usage), so the route is healthy and the failure is probe-side.

Expected: a blocking reading whose window has already reset, and which no longer appears in any new observation, should not hold the lock indefinitely. Either apply the freshness rule that already exists for routing (isTerminalShortWindow() / TERMINAL_SHORT_WINDOW_FRESHNESS_MS in src/codex/quota-types.ts) to the policy windows, or drop a carried blocking tuple once no short window has been observed for N windows and degrade the state to unknown (which admits), or surface it as an operator-actionable state instead of a silent permanent blocked.

Reproduction

  1. Have a ChatGPT main account (native Codex App login, authMode: forward, plan prolite) whose 5h window is observed once at >= 98%. Here that was 2026-09-11.
  2. Afterwards, have observations report only the weekly window (the plan/endpoint no longer returns a 5h window).
  3. Wait past shortResetAt; restart the proxy (ocx service repair); run ocx account refresh openai; leave it running for weeks.
  4. Observe GET /api/codex-auth/accounts (header x-opencodex-api-key) still returning {"enabled":true,"state":"blocked"} for __main__, with no resetAt, while weeklyPercent is 0.
  5. Confirm the evidence is frozen: ~/.opencodex/codex-quota-cache.json -> mainPolicyQuota.quota.shortObservedAt stays at 2026-09-11 while updatedAt keeps advancing with weekly observations.

Verification after the workaround below: the same endpoint returns {"enabled":true,"state":"ready"} and the state does not regress, because weekly-only observations never re-add the short tuple.

Version

2.71.0

Operating system

macOS 26.6.2 (25G83), arm64

Provider and model

openai / Codex account pool (authMode: forward, main account plan prolite); models gpt-6-astra, gpt-5.6-luna

Logs or error output

GET /api/codex-auth/accounts (unchanged across restarts for 19 days):

{"id":"__main__","isMain":true,"plan":"prolite","paused":false,"needsReauth":false,
 "mainAccountHardLock":{"enabled":true,"state":"blocked"},
 "quota":{"weeklyPercent":0,"weeklyResetAt":1791303196,"resetCredits":1,"updatedAt":1790698396180}}

~/.opencodex/codex-quota-cache.json -> mainPolicyQuota.quota:

{"updatedAt":1790698396180,
 "weeklyPercent":0,"weeklyResetAt":1791303196,"resetCredits":1,
 "shortPercent":100,
 "shortObservedAt":1789098096600,
 "shortResetAt":1789111776,
 "shortWindowSeconds":18000}

shortObservedAt = 2026-09-11T03:41:36Z, shortResetAt = 2026-09-11T07:29:36Z. The updatedAt field keeps moving because weekly observations keep landing, so the record looks fresh while the only honest age signal (shortObservedAt) is never consulted by the lock path.

ocx doctor:

WHAM reachability
  --  https://chatgpt.com/backend-api/wham/usage
       error=timeout, 8010ms, authenticated

Redacted configuration

{
  "openaiProviderTierVersion": 1,
  "activeCodexAccountId": "chatgpt-##########",
  "codexAccounts": [
    { "id": "chatgpt-##########", "plan": "prolite", "isMain": false, "logLabel": "pe17f2a", "alias": "<secondary>" }
  ],
  "providers": {
    "openai": { "adapter": "openai-responses", "baseUrl": "https://chatgpt.com/backend-api/codex", "codexAccountMode": "pool", "authMode": "forward" }
  }
}

codexMainAccountHardLock is absent, i.e. the default (enabled). The main account is __main__ (native Codex App login) and is not listed in codexAccounts.

Checks

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

Workaround that unblocked this machine: with the proxy stopped, delete shortPercent, shortObservedAt, shortResetAt and shortWindowSeconds from mainPolicyQuota.quota in ~/.opencodex/codex-quota-cache.json (keep the weekly fields), then start. mainAccountHardLock.state immediately becomes ready and stays there. Setting codexMainAccountHardLock: false also works but gives up the protection entirely.

Related: #3552 (window-aware main-account hard lock), #6153 (pace hard-lock recovery and quota retries), #5743 (bundle L4 — 98% main lock).

Activity

  1. github-actions commented on Sep 29, 2026

    @github-actions
    Contributor

    Issue reopened

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

  2. added
    bugSomething isn't working
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    serviceService lifecycle (WinSW/launchd/scheduler)
    on Sep 29, 2026
  3. lidge-jun commented on Sep 30, 2026

    @lidge-jun
    Owner

    Fixed by #6287 (merged as f69918c). The main-account hard lock now releases on a fresh 5h reading below the threshold, or when WHAM explicitly reports the 5h window as absent and the weekly window is measured. Partial readings and elapsed reset times still keep it locked. It should clear on the first usage refresh after you update. If it stays stuck on a build that includes this fix, please reopen with the ocx status quota section.

  4. Howard-Jerry commented on Oct 11, 2026

    @Howard-Jerry
    Contributor

    Real-world confirmation that #6287 released a previously-stuck lock, observed on this machine (macOS, arm64).

    Timeline on @Howard-Jerry machine:

    • Installed build at the time: @bitkyc08/opencodex@2.73.0 (pre-fix(codex): retire main hard lock on authoritative absent 5h window #6287). With codexMainAccountHardLock: true, the main account stayed blocked across proxy restarts and ocx account refresh openai, consistent with this issue: carried short evidence with only weekly observations. End users saw only the bare client error exceeded retry limit, last status: 429 Too Many Requests on the Responses path (the informative policy body did render on a non-streaming image call: "Codex main account is blocked by the 98% main-account quota policy...").

    • After upgrading to 2.83.0 and keeping the lock enabled, a forced ocx account refresh openai produced the release immediately, exactly matching the fix(codex): retire main hard lock on authoritative absent 5h window #6287 semantics (primary absent + measured weekly):

      main w***8@gmail.com pro weekly 0% resets 2026-10-17T13:54:40.000Z

      GET /api/settings → "mainAccountHardLock":{"enabled":true,"state":"ready","thresholds":{"short":90,"long":98}}

    • Model-path smoke test (bundled codex-cli 0.162.0-alpha.17.2, gpt-6.1-sol, low effort) completed normally while the lock was enabled and ready.

    So: no reopen from us — the release-on-fresh-observation behavior is verified working on 2.83.0. One note for future searchers landing here via the exceeded retry limit, last status: 429 text on pre-fix builds: the immediate local workaround is codexMainAccountHardLock → off (Settings), and the durable fix is upgrading to a build that includes #6287. If it ever gets stuck again on a current build, this thread asks for the ocx status quota section — attaching ours for reference.

    Thanks for the fast fix and the clear release semantics.

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 workingserviceService lifecycle (WinSW/launchd/scheduler)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions