fix(codex): recover from refresh-token rotation races instead of dying#20
Merged
Merged
Conversation
ChatGPT rotates the refresh token on every refresh, and the single-flight guard only covers one process. When two openscience processes (CLI + workspace server) race a refresh — common while requests retry against an exhausted usage limit — the loser persists nothing and keeps a revoked token, so every later refresh fails even after the usage limit resets. The only way out was noticing yourself that you had to re-login. The loader now treats a failed refresh as possibly-stale local state: re-read persisted auth (the racing winner already stored the rotated pair), adopt a still-valid access token or retry the refresh with the rotated refresh token, and only then fail — with an actionable 'reconnect with openscience auth login' message instead of a raw 'Token refresh failed: 400'.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Jul 5, 2026
Closed
Aayam Bansal (aayambansal)
added a commit
that referenced
this pull request
Jul 7, 2026
…ot (#128) - clearSession() removes the synced credential artifacts (synced-env.json, openscience-synced.json) and unsets the env BEFORE deleting the session file. A crash after unlinking the session but before removing synced-env.json otherwise left preload-env.ts replaying the managed thk_ key on the next boot, so the signed-out account's wallet kept being debited. (#37) - Seed syncedSecretValues from the on-disk snapshot at import. preload-env.ts replays synced-env.json into process.env but never seeded this set, so on a fresh process where no in-process sync runs (the common steady state) the set was empty and a disk-replayed synced secret that isn't a thk_ value was neither stripped from subprocess env nor masked by redactSecrets(). (#20)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Users whose Codex (Sign in with ChatGPT) usage limit gets exhausted end up with a permanently broken
openai-codexprovider — even after the ChatGPT limit resets. Root cause: ChatGPT rotates the refresh token on every refresh, and our single-flight guard only covers one process. While requests retry against an exhausted limit, the CLI and the workspace server race the refresh; the loser keeps a revoked refresh token, and from then on every refresh throwsToken refresh failed: 400until the user figures out they must disconnect and re-login.The loader's refresh path now:
openscience auth login→ Codex — Sign in with ChatGPT') instead of the raw 400Verified: turbo typecheck clean; full backend suite 828/828; codex + billing-gate tests 20/20. The loader fetch closure has no unit harness (existing tests cover only the exported JWT helpers) — noted for future work.