Classify Codex app-server chatgpt-auth-required as an auth error

When auth.json holds only an OPENAI_API_KEY (no ChatGPT tokens), the
app-server RPC rejects account/rateLimits/read with "chatgpt
authentication required to read rate limits". That string matched none
of CODEX_AUTH_ERROR_PATTERNS, so fetchCodexRateLimits fell through to
the hidden PTY /status probe, which cannot render usage for such
accounts and burned the full 15s PTY timeout on every refresh cycle,
surfacing as a permanent "Refresh failed — PTY timeout" status chip.

Classify the message as an auth error so the RPC result is returned
directly (fast, accurate) and no PTY is spawned.
This commit is contained in:
Brennan Benson
2026-07-14 14:16:44 -07:00
parent 1b3f281ff5
commit 7957fc75bb
2 changed files with 50 additions and 1 deletions
@@ -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 =
+5 -1
View File
@@ -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')