Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/main/rate-limits/codex-fetcher-auth-errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,51 @@ describe('fetchCodexRateLimits auth errors', () => {
expect(ptySpawnMock).not.toHaveBeenCalled()
})

it('returns the app-server chatgpt-auth-required error without spawning the PTY probe', async () => {
const rpcChild = makeRpcChild()
const authError = 'chatgpt authentication required to read rate limits'

childSpawnMock.mockReturnValue(rpcChild)
rpcChild.stdin.write.mockImplementation((line: string) => {
const msg = JSON.parse(line) as { id?: number; method?: string }
if (msg.method === 'initialize') {
setTimeout(() => {
rpcChild.stdout.emit(
'data',
Buffer.from(`${JSON.stringify({ jsonrpc: '2.0', id: msg.id, result: {} })}\n`)
)
}, 0)
}
if (msg.method === 'account/rateLimits/read') {
setTimeout(() => {
rpcChild.stdout.emit(
'data',
Buffer.from(
`${JSON.stringify({
jsonrpc: '2.0',
id: msg.id,
error: { code: -32600, message: authError }
})}\n`
)
)
}, 0)
}
})

const resultPromise = fetchCodexRateLimits()
await vi.advanceTimersByTimeAsync(1)
await vi.advanceTimersByTimeAsync(1)

await expect(resultPromise).resolves.toMatchObject({
provider: 'codex',
session: null,
weekly: null,
status: 'error',
error: authError
})
expect(ptySpawnMock).not.toHaveBeenCalled()
})

it('preserves Codex PTY auth errors when the CLI exits before status is available', async () => {
const ptyHandlers: { onData?: (data: string) => void; onExit?: () => void } = {}
const authError =
Expand Down
14 changes: 14 additions & 0 deletions src/renderer/src/components/status-bar/tooltip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,20 @@ describe('provider usage error copy', () => {
)
})

it('classifies the Codex chatgpt-auth-required rate-limits read error as auth, not Limited', () => {
// Why: the message mentions "rate limits" only as the object it failed to
// read; labeling it "Limited" would wrongly imply the user hit a limit.
const p = provider({
provider: 'codex',
error: 'chatgpt authentication required to read rate limits'
})

expect(getProviderUsageStatusLabel(p)).toBe('Refresh failed')
expect(getProviderUsageErrorMessage(p)).toBe(
'Codex usage could not be refreshed. Agent sessions may still be signed in.'
)
})

it('keeps generic OAuth and network failures as raw refresh details', () => {
const oauth = provider({ error: 'OAuth API returned 500' })
const network = provider({ error: 'Network error while refreshing OAuth usage: ECONNRESET' })
Expand Down
9 changes: 8 additions & 1 deletion src/renderer/src/components/status-bar/usage-error-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ export function getProviderDisplayName(provider: ProviderRateLimits['provider'])
}

function isUsageRateLimitError(message: string | null): boolean {
return Boolean(message && /\brate[- ]?limits?\b|\brate[- ]?limited\b/i.test(message))
// Why: Codex app-server's "chatgpt authentication required to read rate
// limits" mentions rate limits only as the thing it could not read; treat
// authentication-required failures as auth, never as the user being limited.
if (!message || /\bauthentication required\b/i.test(message)) {
return false
}
return /\brate[- ]?limits?\b|\brate[- ]?limited\b/i.test(message)
}

const USAGE_AUTH_ERROR_PATTERNS = [
Expand All @@ -45,6 +51,7 @@ const USAGE_AUTH_ERROR_PATTERNS = [
/\bauth (?:is missing|tokens are missing|does not expose)\b/i,
/\bunauthori[sz]ed\b/i,
/\bunauthenticated\b/i,
/\bauthentication required\b/i,
/\bplease reauthenticate\b/i,
/\bsign in\b/i,
/\blogged in to another account\b/i,
Expand Down
6 changes: 5 additions & 1 deletion src/shared/codex-auth-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ const CODEX_AUTH_ERROR_PATTERNS = [
/please reauthenticate/i,
/not logged in/i,
/token data is not available/i,
/auth (?:is missing|tokens are missing|does not expose)/i
/auth (?:is missing|tokens are missing|does not expose)/i,
// Why: app-server rejects account/rateLimits/read with this when auth.json
// holds only an API key; without classification the fetcher falls through to
// a hidden PTY probe that can only time out (15s) on every refresh.
/chatgpt authentication required/i
]
const ANSI_ESCAPE_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*[a-zA-Z]`, 'g')

Expand Down
Loading