From c45dc62eaff9c6163bf3e173ca433bb09d21f3e6 Mon Sep 17 00:00:00 2001
From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
Date: Mon, 20 Jul 2026 16:18:43 -0700
Subject: [PATCH] fix(codex): flag signed-out system default (#9619)
* fix(codex): flag signed-out system default
* fix(codex): distinguish missing sign-in warnings
* test(codex): preserve system OAuth auth warning
---
.../src/components/settings/AccountsPane.tsx | 75 +++++++++++--------
.../codex-account-auth-warning.test.ts | 44 ++++++++++-
.../settings/codex-account-auth-warning.ts | 13 +++-
src/renderer/src/i18n/locales/en.json | 3 +-
src/renderer/src/i18n/locales/es.json | 3 +-
src/renderer/src/i18n/locales/ja.json | 3 +-
src/renderer/src/i18n/locales/ko.json | 3 +-
src/renderer/src/i18n/locales/zh.json | 3 +-
8 files changed, 105 insertions(+), 42 deletions(-)
diff --git a/src/renderer/src/components/settings/AccountsPane.tsx b/src/renderer/src/components/settings/AccountsPane.tsx
index a0f80f5922a..651b03140b3 100644
--- a/src/renderer/src/components/settings/AccountsPane.tsx
+++ b/src/renderer/src/components/settings/AccountsPane.tsx
@@ -414,21 +414,20 @@ export function AccountsPane({
// WSL falls back to the generic label.
const systemCodexIdentity =
accountRuntime.runtime === 'host' ? codexAccounts.systemDefault : undefined
- // Why: the auth warning is derived from the desktop's own rate-limit poll;
- // with a remote owner it would misattribute local auth state to the server.
- const activeCodexAuthWarning =
- codexAccountsLoaded && !isRemoteAccountScope
- ? getCodexAccountAuthWarning({
- limits: codexRateLimits,
- target: codexRateLimitTarget,
- runtime: accountRuntime,
- activeAccountId: activeCodexAccountId,
- accountId: activeCodexAccountId,
- authKind: activeCodexAccountId === null ? systemCodexIdentity?.authKind : undefined
- })
- : null
- const systemCodexNeedsReauthentication =
- activeCodexAccountId === null && Boolean(activeCodexAuthWarning)
+ // Why: remote snapshots own their system-default identity, but the desktop's
+ // rate-limit poll must not be misattributed to a remote account owner.
+ const activeCodexAuthWarning = codexAccountsLoaded
+ ? getCodexAccountAuthWarning({
+ limits: isRemoteAccountScope ? null : codexRateLimits,
+ target: codexRateLimitTarget,
+ runtime: accountRuntime,
+ activeAccountId: activeCodexAccountId,
+ accountId: activeCodexAccountId,
+ authKind: activeCodexAccountId === null ? systemCodexIdentity?.authKind : undefined
+ })
+ : null
+ const systemCodexMissingSignIn = activeCodexAuthWarning === 'missing-sign-in'
+ const systemCodexNeedsSignIn = activeCodexAccountId === null && Boolean(activeCodexAuthWarning)
const accountRuntimeUnavailable =
accountRuntime.runtime === 'wsl' && !wslAvailable && !wslCapabilitiesLoading
@@ -1080,16 +1079,22 @@ export function AccountsPane({
- {activeCodexAccountId
+ {systemCodexMissingSignIn
? translate(
- 'auto.components.settings.AccountsPane.75ca9b718e',
- 'Codex reported that the active account needs a fresh sign-in. Re-authenticate it before starting new Codex sessions.'
- )
- : translate(
- 'auto.components.settings.AccountsPane.e4a28e8894',
- 'Codex reported that the {{value0}} login needs a fresh sign-in. Sign in again before starting new Codex sessions.',
+ 'auto.components.settings.AccountsPane.codexSystemDefaultNeedsSignIn',
+ 'No Codex sign-in was found for {{value0}}.',
{ value0: accountRuntimeSentenceLabel }
- )}
+ )
+ : activeCodexAccountId
+ ? translate(
+ 'auto.components.settings.AccountsPane.75ca9b718e',
+ 'Codex reported that the active account needs a fresh sign-in. Re-authenticate it before starting new Codex sessions.'
+ )
+ : translate(
+ 'auto.components.settings.AccountsPane.e4a28e8894',
+ 'Codex reported that the {{value0}} login needs a fresh sign-in. Sign in again before starting new Codex sessions.',
+ { value0: accountRuntimeSentenceLabel }
+ )}
) : null}
@@ -1156,7 +1161,7 @@ export function AccountsPane({
}
disabled={codexAction !== 'idle' || accountRuntimeUnavailable}
className={`flex w-full items-center justify-between gap-3 rounded-md border px-3 py-2.5 text-left transition-colors ${
- systemCodexNeedsReauthentication
+ systemCodexNeedsSignIn
? 'border-destructive/50 bg-destructive/5'
: systemCodexActive
? 'border-foreground/20 bg-accent/15'
@@ -1179,7 +1184,7 @@ export function AccountsPane({
{translate('auto.components.settings.AccountsPane.e74831fb6b', 'Active')}
) : null}
- {systemCodexNeedsReauthentication ? (
+ {systemCodexNeedsSignIn ? (
- {systemCodexNeedsReauthentication
- ? translate(
- 'auto.components.settings.AccountsPane.fd62f37c24',
- 'Codex reported this {{value0}} login is out of date.',
- { value0: accountRuntimeSentenceLabel }
- )
+ {systemCodexNeedsSignIn
+ ? systemCodexMissingSignIn
+ ? translate(
+ 'auto.components.settings.AccountsPane.codexSystemDefaultNeedsSignIn',
+ 'No Codex sign-in was found for {{value0}}.',
+ { value0: accountRuntimeSentenceLabel }
+ )
+ : translate(
+ 'auto.components.settings.AccountsPane.fd62f37c24',
+ 'Codex reported this {{value0}} login is out of date.',
+ { value0: accountRuntimeSentenceLabel }
+ )
: getCodexSystemDefaultSubtitle(
systemCodexIdentity,
accountRuntimeSentenceLabel
diff --git a/src/renderer/src/components/settings/codex-account-auth-warning.test.ts b/src/renderer/src/components/settings/codex-account-auth-warning.test.ts
index dcb14b039b4..4d1631fbaad 100644
--- a/src/renderer/src/components/settings/codex-account-auth-warning.test.ts
+++ b/src/renderer/src/components/settings/codex-account-auth-warning.test.ts
@@ -49,7 +49,22 @@ describe('codex account auth warning', () => {
activeAccountId: 'account-1',
accountId: 'account-1'
})
- ).toContain('access token could not be refreshed')
+ ).toBe('stale-sign-in')
+ })
+
+ it('preserves stale-sign-in warnings for an OAuth system default', () => {
+ expect(
+ getCodexAccountAuthWarning({
+ limits: codexLimits(
+ 'Your access token could not be refreshed. Please log out and sign in again.'
+ ),
+ target: { runtime: 'host', wslDistro: null },
+ runtime: { runtime: 'host' },
+ activeAccountId: null,
+ accountId: null,
+ authKind: 'oauth'
+ })
+ ).toBe('stale-sign-in')
})
it('does not warn for inactive accounts or a different runtime', () => {
@@ -90,6 +105,33 @@ describe('codex account auth warning', () => {
).toBeNull()
})
+ it('warns only when the active system default has no usable login', () => {
+ const getWarning = (authKind: 'none' | 'api-key' | 'oauth') =>
+ getCodexAccountAuthWarning({
+ limits: null,
+ target: { runtime: 'host', wslDistro: null },
+ runtime: { runtime: 'host' },
+ activeAccountId: null,
+ accountId: null,
+ authKind
+ })
+
+ expect(getWarning('none')).toBe('missing-sign-in')
+ expect(getWarning('api-key')).toBeNull()
+ expect(getWarning('oauth')).toBeNull()
+
+ expect(
+ getCodexAccountAuthWarning({
+ limits: null,
+ target: { runtime: 'host', wslDistro: null },
+ runtime: { runtime: 'host' },
+ activeAccountId: 'account-1',
+ accountId: null,
+ authKind: 'none'
+ })
+ ).toBeNull()
+ })
+
it('allows a WSL default account location to receive the active WSL target warning', () => {
expect(
codexRateLimitTargetMatchesAccountRuntime(
diff --git a/src/renderer/src/components/settings/codex-account-auth-warning.ts b/src/renderer/src/components/settings/codex-account-auth-warning.ts
index cefccac3d5c..9db1e3ae8e5 100644
--- a/src/renderer/src/components/settings/codex-account-auth-warning.ts
+++ b/src/renderer/src/components/settings/codex-account-auth-warning.ts
@@ -10,6 +10,8 @@ type AccountRuntime = {
wslDistro?: string | null
}
+type CodexAccountAuthWarning = 'missing-sign-in' | 'stale-sign-in'
+
export function codexRateLimitTargetMatchesAccountRuntime(
target: RateLimitRuntimeTarget,
runtime: AccountRuntime
@@ -30,14 +32,17 @@ export function getCodexAccountAuthWarning(args: {
activeAccountId: string | null
accountId: string | null
authKind?: CodexSystemDefaultIdentity['authKind']
-}): string | null {
+}): CodexAccountAuthWarning | null {
+ if (args.accountId !== args.activeAccountId) {
+ return null
+ }
// Why: app-server reports API-key homes as a ChatGPT-auth error because
// usage is unsupported; that is not a stale sign-in the user can re-auth.
if (args.accountId === null && args.authKind === 'api-key') {
return null
}
- if (args.accountId !== args.activeAccountId) {
- return null
+ if (args.accountId === null && args.authKind === 'none') {
+ return 'missing-sign-in'
}
if (!codexRateLimitTargetMatchesAccountRuntime(args.target, args.runtime)) {
return null
@@ -45,5 +50,5 @@ export function getCodexAccountAuthWarning(args: {
if (args.limits?.status !== 'error' || !isCodexAuthError(args.limits.error)) {
return null
}
- return args.limits.error?.trim() || 'Codex reported that this sign-in needs re-authentication.'
+ return 'stale-sign-in'
}
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index 1d7263b6a1d..1e0b811e0e5 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -4989,7 +4989,8 @@
"remoteScopeAuthContext": "Each account keeps its own sign-in context on {{value0}}.",
"remoteEmptyClaudeAccounts": "No managed Claude accounts on {{value0}}. It uses its system default Claude login; add accounts on that server.",
"remoteEmptyCodexAccounts": "No managed Codex accounts on {{value0}}. It uses its system default Codex login; add accounts on that server.",
- "codexSystemDefaultCustomProvider": "Custom provider — no usage tracked."
+ "codexSystemDefaultCustomProvider": "Custom provider — no usage tracked.",
+ "codexSystemDefaultNeedsSignIn": "No Codex sign-in was found for {{value0}}."
},
"AdvancedPane": {
"40b29e0bf3": "Restart",
diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json
index a10a0ea150a..7ad26a2f327 100644
--- a/src/renderer/src/i18n/locales/es.json
+++ b/src/renderer/src/i18n/locales/es.json
@@ -4966,7 +4966,8 @@
"remoteScopeAuthContext": "Cada cuenta mantiene su propio contexto de inicio de sesión en {{value0}}.",
"remoteEmptyClaudeAccounts": "No hay cuentas de Claude administradas en {{value0}}. Usa su inicio de sesión de Claude predeterminado del sistema; agrega cuentas en ese servidor.",
"remoteEmptyCodexAccounts": "No hay cuentas de Codex administradas en {{value0}}. Usa su inicio de sesión de Codex predeterminado del sistema; agrega cuentas en ese servidor.",
- "codexSystemDefaultCustomProvider": "Proveedor personalizado — no se registra el uso."
+ "codexSystemDefaultCustomProvider": "Proveedor personalizado — no se registra el uso.",
+ "codexSystemDefaultNeedsSignIn": "No se encontró ningún inicio de sesión de Codex para {{value0}}."
},
"AdvancedPane": {
"40b29e0bf3": "Reiniciar",
diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json
index 4531cc490a3..f7e7c05745e 100644
--- a/src/renderer/src/i18n/locales/ja.json
+++ b/src/renderer/src/i18n/locales/ja.json
@@ -4951,7 +4951,8 @@
"remoteScopeAuthContext": "各アカウントは {{value0}} 上に独自のサインインコンテキストを保持します。",
"remoteEmptyClaudeAccounts": "{{value0}} に管理対象の Claude アカウントはありません。システム既定の Claude ログインを使用します。アカウントの追加はそのサーバー上で行ってください。",
"remoteEmptyCodexAccounts": "{{value0}} に管理対象の Codex アカウントはありません。システム既定の Codex ログインを使用します。アカウントの追加はそのサーバー上で行ってください。",
- "codexSystemDefaultCustomProvider": "カスタムプロバイダー — 使用量は追跡されません。"
+ "codexSystemDefaultCustomProvider": "カスタムプロバイダー — 使用量は追跡されません。",
+ "codexSystemDefaultNeedsSignIn": "{{value0}} の Codex サインインが見つかりません。"
},
"AdvancedPane": {
"40b29e0bf3": "再起動",
diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json
index dd155b3a4bf..57adc540b89 100644
--- a/src/renderer/src/i18n/locales/ko.json
+++ b/src/renderer/src/i18n/locales/ko.json
@@ -4951,7 +4951,8 @@
"remoteScopeAuthContext": "각 계정은 {{value0}}에 자체 로그인 컨텍스트를 유지합니다.",
"remoteEmptyClaudeAccounts": "{{value0}}에 관리되는 Claude 계정이 없습니다. 해당 서버의 시스템 기본 Claude 로그인을 사용하며, 계정 추가는 그 서버에서 진행하세요.",
"remoteEmptyCodexAccounts": "{{value0}}에 관리되는 Codex 계정이 없습니다. 해당 서버의 시스템 기본 Codex 로그인을 사용하며, 계정 추가는 그 서버에서 진행하세요.",
- "codexSystemDefaultCustomProvider": "사용자 지정 제공업체 — 사용량을 추적하지 않습니다."
+ "codexSystemDefaultCustomProvider": "사용자 지정 제공업체 — 사용량을 추적하지 않습니다.",
+ "codexSystemDefaultNeedsSignIn": "{{value0}}에 대한 Codex 로그인을 찾을 수 없습니다."
},
"AdvancedPane": {
"40b29e0bf3": "다시 시작",
diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json
index 825f838ddd4..365a23efdf2 100644
--- a/src/renderer/src/i18n/locales/zh.json
+++ b/src/renderer/src/i18n/locales/zh.json
@@ -4951,7 +4951,8 @@
"remoteScopeAuthContext": "每个账户在 {{value0}} 上保留自己的登录上下文。",
"remoteEmptyClaudeAccounts": "{{value0}} 上没有受管理的 Claude 账户。它使用其系统默认的 Claude 登录;请在该服务器上添加账户。",
"remoteEmptyCodexAccounts": "{{value0}} 上没有受管理的 Codex 账户。它使用其系统默认的 Codex 登录;请在该服务器上添加账户。",
- "codexSystemDefaultCustomProvider": "自定义提供商 — 不跟踪使用情况。"
+ "codexSystemDefaultCustomProvider": "自定义提供商 — 不跟踪使用情况。",
+ "codexSystemDefaultNeedsSignIn": "未找到 {{value0}} 的 Codex 登录信息。"
},
"AdvancedPane": {
"40b29e0bf3": "重新启动",