[account/status] Preserve the source of auth and usage plan values
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What version of the Codex App are you using (From “About Codex” dialog)?
Codex App 26.901.22334 (build 7746), bundled codex-cli 0.153.0.
What subscription do you have?
Conflicting transitional state after an unsuccessful renewal: ChatGPT Billing reports paused/Free, while Codex account and usage surfaces report planType: pro. No invoice, payment-method, email, address, or other billing identifiers are included.
What platform is your computer?
Darwin 25.5.0 arm64 arm
What issue are you seeing?
Codex surfaces unqualified planType values as if they formed one authoritative subscription label, although the open client receives plan-related values from different account/usage paths and has no ChatGPT Billing state with which to validate them.
Observed state
- ChatGPT Billing reports that the plan is paused and access is limited to Free.
- Codex account/usage surfaces report
planType: pro. - Codex requests continue to succeed in the session whose usage response reports Pro.
- The product does not explain whether
Promeans a paid subscription, a temporary/grace entitlement, a cached auth claim, or the classification returned by the usage backend.
This is financially material even when requests still work: a user cannot tell whether continued use is expected, whether the label confirms payment, or whether account systems have converged.
How we traced this
The investigation did not begin with source code or an attempt to obtain extra access. It began with a financial event that made the continuing Pro label unexpected.
1. Human trigger (user-reported)
The account holder expected the paid Pro subscription to end. Around renewal time, they saw an attempted OpenAI charge against a payment method whose available balance was insufficient. The banking/payment interface appeared to show the attempt as declined, cancelled, or reversed.
We did not independently inspect the bank account, and no bank evidence is being published. The exact processor status therefore remains a user-reported observation.
The natural expectation was: if renewal did not complete, paid entitlement should either end or the product should clearly identify a temporary grace/retry state. Instead, the account continued to display Pro and serve requests in a session classified as Pro by the usage interface. The account holder wanted to understand and report the discrepancy rather than assume the remaining access had been granted for free.
2. Initial hypotheses
- Renewal actually succeeded despite the bank-side appearance.
- Only the visible Pro badge was stale.
- The local application cached an old account claim.
- OpenAI intentionally provides a payment grace period.
- Billing and entitlement services have not converged.
- One surface is wrong.
We treated these as hypotheses instead of selecting the most favorable explanation.
3. Billing-side checks
We opened ChatGPT Billing and the user-visible invoice. Billing explicitly said the plan was paused and access was limited to Free. The invoice showed no completed payment and an outstanding balance. It did not show a clear retry schedule or one effective end time for the remaining Pro label/access.
This made a normal successful renewal inconsistent with the available billing evidence. It did not distinguish grace period, delayed synchronization, or an entitlement defect.
4. Account and usage checks
We then tested whether Pro was merely a stale decoration. The account surface still displayed Pro, and a normal read-only Codex usage-limit read returned planType: pro. Codex requests continued to succeed in that session.
This ruled out a purely cosmetic badge and a purely local display cache as sufficient explanations for the reported planType. It showed that a remote usage path still classified the account as Pro. Successful requests alone are not evidence of paid Pro entitlement, and none of these observations proves payment.
5. Documentation and public-source trace
Official App Server documentation describes account/read separately from account/rateLimits/read and states that a rate-limit bucket can include a server-returned planType.
Tracing those fields through openai/codex showed that the account-side value comes from auth state, TUI bootstrap reads the account without forcing a token refresh, a rate-limit response can later supply another plan value, and the TUI merges it into an unqualified self.plan_type without retaining provenance. /status receives a separate plan argument but does not use it; the visible Account line renders the auth-derived plan in parentheses.
The /wham/usage route was found in public source, not through packet capture or copied credentials.
Narrowed conclusion
Billing says renewal is unpaid and access is Free/paused, while a remote Codex usage path reports planType: pro and requests continue to succeed. The public client does not preserve enough provenance for the UI to distinguish account/usage classifications from authoritative billing state. The private server-side reason remains unknown.
What steps can reproduce the bug?
The exact account transition depends on server state, but the observed product sequence was:
- Reach a renewal state where ChatGPT Billing reports paused/Free and the visible invoice has no completed payment.
- Open the Codex account/usage surface for the same account.
- Observe
planType: prowhile requests continue to succeed. - Observe that the UI does not identify whether this value came from auth state, the usage service, Billing, or a grace-period policy.
A deterministic client regression can reproduce the provenance loss without a real billing event:
- Bootstrap a ChatGPT account with auth
plan_type = Pro. - Return an
account/rateLimits/readsnapshot withplan_type = Free; also test the mirror case, auth Free and usage Pro. - Render
/status. - Observe that the model does not retain both sources, the Account line can show the auth-derived plan, and the separate plan argument passed to the status card is unused.
What is the expected behavior?
Codex should identify the provenance and scope of every displayed plan value and should not imply that it confirms billing.
A possible UI:
Account: <redacted>
Codex usage tier: Pro (reported by usage service)
Warning: Account plan sources disagree; this does not confirm billing status.
The warning should appear only when independently obtained plan values disagree. If Billing state is unavailable, label the available value by source and avoid presenting it as payment confirmation. The client must not claim a Billing mismatch it cannot observe.
Proposed direction
- Keep auth-plan and usage-plan values separate.
- Track provenance and, if practical, observation time.
- Render account identity separately from
Codex usage tier. - Prefer a fresh usage snapshot for quota behavior while retaining auth state for mismatch detection.
- When Billing state is unavailable, state that limitation without inferring whether payment succeeded.
- If a host later supplies authoritative Billing state, compare it explicitly rather than overloading either plan field.
Acceptance tests
- auth Pro, usage Free: represent both sources and render a mismatch warning
- auth Free, usage Pro: render the mirror mismatch
- auth Pro, usage Pro: no warning
- one source unavailable: label the available source without claiming a mismatch
- no email or billing details required in snapshots or logs
Additional information
Code-level analysis
At commit 956aa3f6372ffd73a0df639beff356b3b664b858:
current_account_updated_notificationobtainsplan_typefrom cached auth.- TUI bootstrap
account/readusesrefresh_token: false. on_rate_limit_snapshot_frommerges the usagesnapshot.plan_typeintoself.plan_typewithout retaining provenance.new_status_output_with_rate_limits_handlereceives that value as_plan_typebut does not use it.StatusHistoryCell::display_linesrenders the auth-derived plan in the Account field.
Evidence boundaries
- We did not inspect private Billing or entitlement-service source.
- We did not packet-capture the app or extract/copy auth tokens.
planType: prowas observed through a product-provided read-only usage-limit call.- We did not mutate subscription, payment methods, invoice, account, or rate-limit state.
- Public evidence cannot distinguish grace period, delayed convergence, or entitlement defect.
- The proposed mismatch fixtures are regression designs, not claimed direct responses from this account.
Related reports
- Codex showed Pro/full usage while inference treated the user as Free
- Usage state differed between web and macOS app surfaces
- Desktop usage display disagreed with backend usage response
Those reports demonstrate the broader authority-conflict class. This report is specifically about loss of plan provenance and the misleading subscription semantics that result.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with on_rate_limit_snapshot_from in codex-rs/tui/src/chatwidget/rate_limits.rs, then trace the account/read flow in codex-rs/tui/src/app_server_session.rs and status rendering in codex-rs/tui/src/status/card.rs. Use the listed auth/usage fixtures as regression cases; done means both sources remain distinguishable, mismatch warnings follow the acceptance tests, and unavailable sources are labeled without implying billing confirmation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend-api-design, cli, testing
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100