diff --git a/src/cli/handlers/account-list-format.ts b/src/cli/handlers/account-list-format.ts
index 227c96fc3aa..59df0c6b3a4 100644
--- a/src/cli/handlers/account-list-format.ts
+++ b/src/cli/handlers/account-list-format.ts
@@ -37,7 +37,7 @@ export function formatAccountsBlock(label: string, block: AccountsBlock): string
const lines = block.accounts.map(
(account) =>
` ${account.email}${activeAccountIds.has(account.id) ? ' (active)' : ''}${
- account.needsSignIn ? ' (sign in again in Orca Settings > Accounts)' : ''
+ account.needsSignIn ? ' (sign in again in Orca Settings > AI Provider Accounts)' : ''
}`
)
return `Managed ${label} accounts (${block.accounts.length}):\n${lines.join('\n')}`
diff --git a/src/cli/handlers/account.test.ts b/src/cli/handlers/account.test.ts
index 0308cc6cd0d..e19fd4a8ad2 100644
--- a/src/cli/handlers/account.test.ts
+++ b/src/cli/handlers/account.test.ts
@@ -694,7 +694,7 @@ describe('account CLI handlers', () => {
expect(logSpy).toHaveBeenCalledWith(
expect.stringContaining(
- ' old@example.com (active) (sign in again in Orca Settings > Accounts)\n ok@example.com\n'
+ ' old@example.com (active) (sign in again in Orca Settings > AI Provider Accounts)\n ok@example.com\n'
)
)
})
diff --git a/src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx b/src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx
index 87e632d89ae..07fed72e405 100644
--- a/src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx
+++ b/src/renderer/src/components/status-bar/ClaudeSwitcherMenu.tsx
@@ -37,7 +37,8 @@ import { AccountRuntimeToggle } from './StatusBarAccountControls'
import {
InlineUsageBars,
InlineUsageSignInAction,
- InlineUsageSkeleton
+ InlineUsageSkeleton,
+ isUnavailableInactiveUsage
} from './InlineProviderUsage'
import { ProviderDetailsMenu } from './ProviderDetailsMenu'
import { getClaudeAccountSyncKey } from './provider-account-sync-key'
@@ -352,7 +353,9 @@ export function ClaudeSwitcherMenu({
) : null}
{inactiveUsage?.isFetching && !inactiveUsage.rateLimits ? (
- ) : inactiveUsage?.rateLimits ? (
+ ) : inactiveUsage?.rateLimits &&
+ // Why: the sign-in note above already says this; skip the repeat.
+ !(signInId && isUnavailableInactiveUsage(inactiveUsage.rateLimits)) ? (
snapshot())
const selectClaudeProviderAccount = vi.fn(async () => snapshot())
let storeSettings: GlobalSettings
let activeRuntimeEnvironmentId: string | null = null
+let inactiveClaudeAccounts: unknown[] = []
function account(id: string): ClaudeManagedAccount {
return {
@@ -102,7 +103,7 @@ vi.mock('../../store', () => {
recordFeatureInteraction: vi.fn(),
refreshClaudeRateLimitsForTarget: vi.fn(),
fetchInactiveClaudeAccountUsage: vi.fn(),
- rateLimits: { inactiveClaudeAccounts: [], claudeTarget: { runtime: 'host', wslDistro: null } }
+ rateLimits: { inactiveClaudeAccounts, claudeTarget: { runtime: 'host', wslDistro: null } }
})
const useAppStore = (selector: (value: Record) => unknown): unknown =>
selector(state())
@@ -137,6 +138,7 @@ describe('status bar Claude account that needs a sign-in', () => {
beforeEach(() => {
vi.clearAllMocks()
activeRuntimeEnvironmentId = null
+ inactiveClaudeAccounts = []
storeSettings = {
...getDefaultSettings('/home/me'),
claudeManagedAccounts: [account('old'), account('new')],
@@ -163,6 +165,21 @@ describe('status bar Claude account that needs a sign-in', () => {
expect(toastError).not.toHaveBeenCalled()
})
+ it('does not repeat its sign-in note with a usage sign-in line', async () => {
+ const unavailable = (accountId: string): unknown => ({
+ accountId,
+ isFetching: false,
+ rateLimits: { ...claudeProvider, status: 'error', error: 'Not signed in' }
+ })
+ inactiveClaudeAccounts = [unavailable('old'), unavailable('new')]
+ await openAccounts()
+ expect(screen.getByText('Sign in again to use this account')).toBeTruthy()
+ // Only the row without a sign-in note keeps the usage line.
+ expect(screen.getAllByText('Sign in to see usage')).toHaveLength(1)
+ const newRow = screen.getAllByText('new@example.com').at(-1)!.closest('[role="menuitem"]')
+ expect(newRow?.textContent).toContain('Sign in to see usage')
+ })
+
it('says why a failed sign-in failed', async () => {
reauthenticate.mockRejectedValueOnce(new Error('Claude login exited with code 1.'))
await openAccounts()
diff --git a/src/renderer/src/components/status-bar/inline-usage-bars.test.tsx b/src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
index a6b78d6f849..98b1009da1c 100644
--- a/src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
+++ b/src/renderer/src/components/status-bar/inline-usage-bars.test.tsx
@@ -71,6 +71,22 @@ describe('InlineUsageBars', () => {
expect(markup).toContain('42% used Fable')
})
+ it('asks for a sign-in when another provider has no usage to show', async () => {
+ const { InlineUsageBars } = await import('./StatusBar')
+ const limits: ProviderRateLimits = {
+ provider: 'codex',
+ session: null,
+ weekly: null,
+ updatedAt: Date.now(),
+ error: 'Not signed in',
+ status: 'error'
+ }
+
+ const markup = renderToStaticMarkup()
+
+ expect(markup).toContain('Sign in to see usage')
+ })
+
it('derives the collapsed session label from resetsAt (#5399)', async () => {
const { InlineUsageBars } = await import('./StatusBar')
diff --git a/src/renderer/src/components/status-bar/tooltip.test.ts b/src/renderer/src/components/status-bar/tooltip.test.ts
index c2a2ef35dfb..3cfbded6392 100644
--- a/src/renderer/src/components/status-bar/tooltip.test.ts
+++ b/src/renderer/src/components/status-bar/tooltip.test.ts
@@ -295,7 +295,7 @@ describe('provider usage error copy', () => {
expect(getProviderUsageStatusLabel(p)).toBe('Sign in again')
expect(getProviderUsageErrorMessage(p)).toBe(
- 'The selected Claude account needs you to sign in again. Open Settings > Accounts, or choose System default.'
+ 'The selected Claude account needs you to sign in again. Open Settings > AI Provider Accounts, or choose System default.'
)
})
diff --git a/src/renderer/src/components/status-bar/usage-error-copy.ts b/src/renderer/src/components/status-bar/usage-error-copy.ts
index d9f6cf4557d..be278fc9e9f 100644
--- a/src/renderer/src/components/status-bar/usage-error-copy.ts
+++ b/src/renderer/src/components/status-bar/usage-error-copy.ts
@@ -227,7 +227,7 @@ export function getProviderUsageErrorMessage(p: ProviderRateLimits): string {
case 'missing-credentials':
return translate(
'accounts.claude.usageSignInMessage',
- 'The selected Claude account needs you to sign in again. Open Settings > Accounts, or choose System default.'
+ 'The selected Claude account needs you to sign in again. Open Settings > AI Provider Accounts, or choose System default.'
)
case 'server':
case 'parse':
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index ce8dde6e839..a2c1dbb24a5 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -19439,7 +19439,7 @@
"signInRequired": "Sign in again to use this account",
"signInAgain": "Sign in again",
"usageSignInLabel": "Sign in again",
- "usageSignInMessage": "The selected Claude account needs you to sign in again. Open Settings > Accounts, or choose System default.",
+ "usageSignInMessage": "The selected Claude account needs you to sign in again. Open Settings > AI Provider Accounts, or choose System default.",
"signIn": "Sign in",
"signInNotice": {
"title": "Finish setting up your Claude accounts",