{"record":{"id":"1719ab4fb33e578d","repo":"jlcodes99/cockpit-tools","slug":"codex-pending-oauth-account","errorCode":"CODEX_PENDING_OAUTH_ACCOUNT","errorMessage":"CODEX_PENDING_OAUTH_ACCOUNT","messagePattern":"CODEX_PENDING_OAUTH_ACCOUNT","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/stores/useCodexAccountStore.ts","lineNumber":388,"sourceCode":"    });\n    await emitAccountsChanged({\n      platformId: 'codex',\n      reason: 'delete',\n    });\n    const nextCurrentAccountId = get().currentAccount?.id ?? null;\n    if (previousCurrentAccountId !== nextCurrentAccountId) {\n      await emitCurrentAccountChanged({\n        platformId: 'codex',\n        accountId: nextCurrentAccountId,\n        reason: 'delete',\n      });\n    }\n  },\n\n  refreshQuota: async (accountId: string) => {\n    const account = get().accounts.find((item) => item.id === accountId);\n    if (account && isCodexPendingOAuthAccount(account)) {\n      throw new Error('CODEX_PENDING_OAUTH_ACCOUNT');\n    }\n    try {\n      return await codexService.refreshCodexQuota(accountId);\n    } finally {\n      await get().fetchAccounts();\n      await get().fetchCurrentAccount();\n    }\n  },\n\n  refreshSubscriptionInfo: async (accountId: string) => {\n    const account = await codexService.refreshCodexSubscriptionInfo(accountId);\n    await get().fetchAccounts();\n    await get().fetchCurrentAccount();\n    return account;\n  },\n\n  refreshAllQuotas: async () => {\n    const successCount = await codexService.refreshAllCodexQuotas();","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/stores/useCodexAccountStore.ts#L370-L406","documentation":"refreshQuota refuses to call the backend for Codex accounts that are still in the pending-OAuth state (isCodexPendingOAuthAccount), throwing CODEX_PENDING_OAUTH_ACCOUNT. Quota cannot be refreshed until the OAuth flow completes and the account has real tokens.","triggerScenarios":"Calling refreshQuota(accountId) for an account whose state is pending OAuth (imported mid-OAuth, OAuth flow started but never completed, or an account still awaiting authorization).","commonSituations":"User imported an account but never finished the browser OAuth consent; OAuth callback failed silently; account list refreshed mid-authorization and UI attempted a quota refresh.","solutions":["Complete the OAuth authorization flow for the account before refreshing quota","Check isCodexPendingOAuthAccount(account) in the UI and disable/defer quota refresh","Re-trigger the account import/OAuth flow if it stalled","Remove and re-add the account if the pending state is permanently stuck"],"exampleFix":"// before\nawait store.refreshQuota(accountId);\n// after\nconst account = store.accounts.find(a => a.id === accountId);\nif (account && !isCodexPendingOAuthAccount(account)) {\n  await store.refreshQuota(accountId);\n}","handlingStrategy":"type-guard","validationCode":"const account = store.accounts.find(a => a.id === accountId);\nif (account && isCodexPendingOAuthAccount(account)) {\n  // defer quota refresh until OAuth completes\n}","typeGuard":"function isRefreshable(a?: CodexAccount): boolean {\n  return !!a && !isCodexPendingOAuthAccount(a);\n}","tryCatchPattern":"try {\n  await refreshQuota(accountId);\n} catch (e) {\n  if ((e as Error).message === 'CODEX_PENDING_OAUTH_ACCOUNT') {\n    // prompt user to finish OAuth authorization\n  }\n}","preventionTips":["Disable quota refresh buttons for pending-OAuth accounts","Guide users to complete OAuth before other operations","Detect and clean up permanently stuck pending accounts"],"tags":["oauth","pending-state","quota"],"backgroundTag":"oauth-flow-incomplete","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}