Skip to content

Spark model-specific 5h header window is stored as account-level short quota on Pro accounts #4122

Description

@lidge-jun

Client or integration

OpenCodex dashboard

Area

Authentication and account pool

Summary

Two quota observation paths write into the same per-account snapshot and disagree about what a 5-hour window means.

  • The WHAM path (parseUsageQuota, src/codex/quota.ts) treats a Pro account correctly: the main rate_limit.primary_window is weekly-only, and the GPT-5.3-Codex-Spark 5h/weekly windows from additional_rate_limits (metered_feature codex_bengalfox) are stored only in customWindows.
  • The response-header path (parseUpstreamQuotaHeaders via applyAccountQuotaFromUpstreamHeaders, called from quota-auto-refresh.ts, responses/core.ts, responses/compact.ts) has no model context. When the routed model is Spark, the upstream response headers describe the model-specific 5h limit as the primary window, and the parser stores it as the account-level shortPercent / shortWindowSeconds.

Result: in a pool of Pro accounts, only the account that recently served Spark traffic shows an account-level "5h" quota bar; the others show none. Verified live against backend-api/wham/usage for three Pro accounts on the same pool — all three return an identical structure (primary window 604800s weekly, secondary null, Spark 5h+weekly in additional_rate_limits), yet only the Spark-serving account carries account-level shortPercent 4 with shortWindowSeconds 18000, and that tuple matches the Spark 5h window exactly (same percent, same reset_at). Expected: model-specific limit readings must not occupy the account-level short-window slot, so Pro accounts with identical upstream limits display identically.

Reproduction

  1. Configure a Codex account pool with two or more Pro accounts; pin/select account A.
  2. Route a request for model gpt-5.3-codex-spark through account A with non-zero usage.
  3. The upstream response headers carry the Spark governing limit as primary (x-codex-primary-window-minutes: 300); applyAccountQuotaFromUpstreamHeaders stores it as account A's account-level short window.
  4. Compare with account B, which never served Spark: no account-level short window. The dashboard shows a "5h" quota only for account A, while wham/usage for A and B is structurally identical (weekly-only primary plus Spark additional windows).

A deterministic regression test can replay synthetic headers through applyAccountQuotaFromUpstreamHeaders — for example x-codex-primary-used-percent: 4, x-codex-primary-window-minutes: 300, x-codex-secondary-used-percent: 21, x-codex-secondary-window-minutes: 10080 — and assert the account-level slot is written with no model attribution, without credentials or upstream requests.

Version

2.49.0

Operating system

macOS 27.0

Provider and model

openai (ChatGPT Codex backend, account pool) / gpt-5.3-codex-spark

Logs or error output

wham/usage per account (three Pro accounts, same pool, same minute):
  A: primary_window 604800s/21%  additional GPT-5.3-Codex-Spark 5h 4%  -> cache: shortPercent 4, shortWindowSeconds 18000
  B: primary_window 604800s/29%  additional GPT-5.3-Codex-Spark 5h 0%  -> cache: no short window
  C: primary_window 604800s/24%  additional GPT-5.3-Codex-Spark 5h 0%  -> cache: no short window
A.shortResetAt == A.customWindows["GPT-5.3-Codex-Spark 5h"].resetAt (exact match)

Screenshots and supporting files

No screenshots; the state above is read from codex-quota-cache.json and live wham/usage responses. Related: #4007 fixed the inverse transition (header updates clearing Spark customWindows); this issue is the same collision in the other direction (Spark header readings creating an account-level window).

Suggested fix path:

  1. Thread the routed model (or a model-limit hint) from the header-path callers that know it (responses/core.ts, responses/compact.ts) into applyAccountQuotaFromUpstreamHeaders; when the routed model belongs to a known model-specific limit family (codex_bengalfox / GPT-5.3-Codex-Spark), store the short primary window under customWindows instead of the account-level shortPercent.
  2. Alternatively or additionally, reconcile against the cached customWindows: if a GPT-5.3-Codex-Spark 5h entry with the same resetAt already exists, attribute the header primary reading to that model window.
  3. Refresh the stale comment in parseUpstreamQuotaHeaders ("Pro stays weekly-only") to describe the model-specific case.
  4. Regression test beside the existing quota tests: Spark-model header updates must not create account-level shortPercent, while a genuine account-level 5h primary on a non-Spark model still must.

Redacted configuration

{
  "providers": { "openai": { "codexAccountMode": "pool", "authMode": "forward" } },
  "activeCodexAccountPinned": "<account A>",
  "showCodexSparkQuota": false
}

Checks

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

Activity

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