Windows Desktop: fresh ChatGPT refresh token is rejected as refresh_token_invalidated on its first isolated refresh, causing false logout after unrelated 401

Open 💬 4 comments Opened Aug 21, 2026 by 995616149-crypto
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

26.818.3698.0

What subscription do you have?

ChatGPT Plus

What platform is your computer?

Windows 11 x64

What issue are you seeing?

Codex Desktop repeatedly returns to the ChatGPT login screen even though the current OAuth access token is still valid.

The account is confirmed to be a ChatGPT Plus personal account:

  • /backend-api/wham/accounts/check returns HTTP 200
  • plan_type = plus
  • structure = personal
  • local Codex app-server account/read returns planType = plus

The reproducible failure chain in the Desktop logs is:

  1. Desktop starts in a valid authenticated state:

authenticatedAccountPresent=true

  1. Desktop sends:

GET /backend-api/accounts/{account_id}/settings

  1. That request returns HTTP 401 while:

hadToken=true
tokenSource=cached

  1. The generic auth wrapper treats the 401 as an authentication failure and forces:

refreshToken=true

  1. The refresh fails and app-server reports:

authMethod=chatgpt
hasToken=false
nullReason=auth_token_missing

  1. Account lookup then becomes:

authenticatedAccountPresent=false
failureType=account_info_token_unavailable

  1. Desktop returns to the ChatGPT login screen.

However, after Desktop has already returned to the login screen, the same access token stored in ~/.codex/auth.json still successfully authenticates against:

GET /backend-api/wham/usage

and returns HTTP 200.

Therefore this appears to be a false/in-memory logout rather than an actual access-token expiration.

An earlier occurrence of the /accounts/{account_id}/settings 401 returned:

Must use workspace account for this operation

even though the account is confirmed as plus / personal.

I also performed a strict isolated refresh-token test. After a completely fresh ChatGPT login, with Desktop closed, zero other Codex processes running, exactly one app-server process, and exactly one refresh attempt, the newly issued refresh token was rejected with:

refresh_token_invalidated

The first isolated refresh returned no auth token, last_refresh did not update, and the refresh token did not rotate.

So signing out and signing back in does not resolve the issue.

What steps can reproduce the bug?

  1. Sign in to Codex Desktop using a ChatGPT Plus personal account.
  1. Confirm the account is initially authenticated and usable.
  1. Use the Desktop app normally or wait after startup.
  1. Desktop eventually sends:

GET /backend-api/accounts/{account_id}/settings

  1. The request returns HTTP 401 while the cached access token is still present.
  1. Desktop automatically forces an authentication refresh.
  1. The refresh path returns no auth token and the logs show:

auth_token_missing

  1. Desktop then returns to the "Log in to ChatGPT" screen.
  1. Without logging in again, read the existing access token from ~/.codex/auth.json and use it against:

GET https://chatgpt.com/backend-api/wham/usage

  1. The request still returns HTTP 200, demonstrating that the access token is valid while Desktop considers the account logged out.

I also reproduced the refresh-token failure independently:

  1. Fully close Desktop.
  2. Confirm zero ChatGPT/Codex processes are running.
  3. Perform a fresh official ChatGPT login using Codex.
  4. Confirm a newly issued refresh token exists.
  5. Start exactly one Codex app-server.
  6. Perform exactly one refresh request.
  7. The first refresh fails with:

refresh_token_invalidated

No second app-server or Desktop process is running during this isolation test.

What is the expected behavior?

For a ChatGPT Plus personal account:

  1. Desktop should not request workspace-only account settings unless the account actually supports that endpoint.
  1. If /accounts/{account_id}/settings returns 401 because the endpoint is unavailable for a personal account, that error should be isolated to that feature/query and should not invalidate the entire ChatGPT session.
  1. A still-valid cached access token should not be discarded merely because an unrelated authenticated endpoint returned HTTP 401.
  1. If token refresh fails while the current access token is still valid, Desktop should continue using the valid access token instead of immediately transitioning to auth_token_missing.
  1. A newly issued refresh token should be usable for its first legitimate refresh attempt.
  1. Desktop should remain signed in and usable instead of returning to the login screen.

Additional information

Additional diagnostics already performed:

  • Account backend classification: Plus / personal
  • Local app-server classification: Plus
  • Existing access token remains valid after the UI logout (/wham/usage = HTTP 200)
  • Fresh login does not resolve the problem
  • Cross-process refresh race was ruled out with a strict single-process isolation test
  • Proxy/network connectivity is working normally
  • Recreating the local Codex state did not resolve the issue
  • The problem reproduces with newly created Work sessions, so it is not specific to an old conversation

I can provide sanitized Desktop logs if needed.

For privacy/security, access tokens, refresh tokens, account IDs, user IDs, and email addresses have been omitted.

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 6 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #39189
  • #39850
  • #39199
  • #39696

Powered by Codex Action

ooouuu7997-hue · 6 days ago

我也一样

zipapoker2025-netizen · 2 days ago

Additional data point that isolates this further: on my machine the failing refresh is not preceded by any 401 at all. The brand-new refresh token is rejected on its first ever use, with no triggering /accounts/{account_id}/settings failure in the picture.

Environment

  • Windows 11 25H2 (10.0.26200), x64
  • Codex Desktop 26.820.7780.0 (MSIX from the Store listing 9PLM9XGG6VKS)
  • app_server.client_version=26.820.60940, codex core 0.150.0
  • ChatGPT plan auth (auth_mode = "chatgpt"), no OPENAI_API_KEY set

What I did

Full clean re-auth: killed the entire process tree (8 ChatGPT.exe + 1 codex.exe, verified 0 remaining), deleted %USERPROFILE%\.codex\auth.json, relaunched, signed in through the browser flow.

The file was written correctly — auth.json mtime updated, last_refresh set to the login time, and the refresh_token sha256 differs from the previous one. So this is not a stale-file / failed-write problem.

Timeline (single session, local time UTC+8)

| Time | Event |
|---|---|
| 14:21:34 | codex_login::server received login callback, state_valid=true |
| 14:21:35 | oauth token exchange succeeded status=200 OK; Reloaded auth, changed: true; auth.json written |
| 14:21:38 | GET /backend-api/codex/models200 |
| 14:21:38 | POST /backend-api/ps/apps/batch200 |
| 14:21:41 | POST /backend-api/codex/analytics-events/events200 |
| 14:21:47 | GET /backend-api/codex/models200 (auth_header_attached=true auth_mode="Chatgpt") |
| 14:22:09 | user opens an existing thread → thread/resume |
| 14:22:10 | POST /backend-api/codex/analytics-events/events200 ×2 |
| 14:22:11 | getAuthStatusPOST https://auth.openai.com/oauth/token401 |

ERROR codex_login::auth::manager
app_server.request{otel.name="getAuthStatus" rpc.method="getAuthStatus"
  rpc.request_id=desktop-auth:babddd54-90a9-4a1a-8ef6-85ba49a1a278
  app_server.client_name="Codex Desktop" app_server.client_version="26.820.60940"}:
Failed to refresh token: 401 Unauthorized: {
  "error": {
    "message": "Your refresh token has been invalidated. Please try signing in again.",
    "type": "invalid_request_error",
    "param": null,
    "code": "refresh_token_invalidated"
  }
}

Lifetime of the brand-new refresh token: 36 seconds, on its first use.

That oauth/token 401 is the only non-2xx response and the only ERROR row in the entire session's logs_2.sqlite. Every preceding request returned 200.

The access token stays valid

The access token issued in the same exchange decodes to exp = 10 days out (2026-09-05), and it was still being accepted with 200 twelve seconds before the refresh failed. So the app discards a working credential purely because the refresh path failed.

Ruling out sibling clients

I saw #22903-style multi-client rotation suggested elsewhere, so I checked: enumerating Win32_Process with full command lines at the moment of failure showed exactly one codex.exe app-server (spawned by the desktop app itself), plus the ChatGPT.exe renderer/GPU/utility children. Nothing else on the box held the Windows credential.

I do have a Codex CLI running under WSL, but it is a completely separate ~/.codex/auth.json with a different refresh token, it polls /models every 3 minutes returning 200 OK continuously across the whole window (including at 14:21:46, 11s after the Windows login), and its auth.json has not been rewritten since it logged in the previous day — i.e. it never refreshes at all. It is unaffected before, during, and after the Windows failure.

Notes on the suggested workarounds

Deleting auth.json and re-authenticating does not help — that is exactly what produced the timeline above. The replacement token dies the same way, in well under a minute, on first use.

rememberBr · 1 day ago

I can reproduce the same failure on a newer current Microsoft Store build, and it also reproduces with two different ChatGPT accounts.

Environment

  • Windows 10 Pro 22H2, build 19045.7548, x64
  • Microsoft Store package: OpenAI.Codex 26.820.9563.0 (Status: Ok)
  • Bundled app-server: 0.150.0-alpha.8 (logs report client_version=0.150.0)
  • Standalone Codex CLI: 0.150.1 (continues to work)
  • Microsoft Store / winget currently offers no newer package for product 9PLM9XGG6VKS

Fresh isolated reproduction

Before the test I:

  1. Completely closed Codex Desktop and Codex CLI.
  2. Removed/renamed %USERPROFILE%\.codex\auth.json and its backup.
  3. Removed/renamed the complete %APPDATA%\Codex\web\Codex Chromium profile.
  4. Confirmed the new Chromium profile was freshly created for this launch.
  5. Used a fixed proxy route and verified auth.openai.com and chatgpt.com had the same public egress IP and Cloudflare colo.
  6. Started Desktop alone and completed browser OAuth.

Relevant UTC timeline from the fresh run:

07:34:48 received login callback; state_valid=true
07:34:48 starting OAuth token exchange
07:34:49 OAuth token exchange succeeded: 200 OK
07:34:51 auth reloaded; changed=true
07:35:03 getAuthStatus -> Reloading auth -> Refreshing token
07:35:08 refresh failed: 401 Unauthorized
          code: refresh_token_invalidated
          message: Your refresh token has been invalidated. Please try signing in again.

Immediately before the forced refresh, Desktop also logged:

GET /backend-api/accounts/{account_id}/settings -> 401
hadToken=true
Missing valid access token or actor biscuit

The UI then returned to the login screen.

The standalone CLI was only started at 07:35:26, after Desktop had already received refresh_token_invalidated at 07:35:08. Therefore a local Desktop/CLI refresh race was not present in this reproduction.

The same behavior was reproduced with a second ChatGPT account on the same clean Desktop profile, so this does not appear to be specific to one account's token family.

Additional controls

  • ChatGPT Web works.
  • Standalone Codex CLI works.
  • Rebooting Windows does not help.
  • Recreating both the auth file and the complete Desktop Chromium profile does not help.
  • Changing from rule-based proxy routing to one fixed egress does not help.
  • No WSL Codex process or second Desktop process was running during the isolated reproduction.

This evidence is consistent with the issue's proposed failure chain: a non-session-specific 401 from the account settings request causes Desktop to force an immediate refresh, and the first refresh of a newly issued token is rejected, after which Desktop discards the otherwise fresh login state.

No tokens, account IDs, user IDs, email addresses, or public IP addresses are included here. I can provide a narrowly filtered and sanitized log excerpt if maintainers need it.