mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 08:02:12 +00:00
fix(rate-limits): a malformed Gemini oauth_creds.json is a failed read, not absent
readGeminiCredentials() returned null both when the file was missing and when it was present but did not match the credential contract. The caller turns null into status 'unavailable' / "credentials not found", so a corrupt or wrong-typed oauth_creds.json was reported to the user as signed out — the failed read became an absent one before the shared-quota rule could see it, and absent takes the discard path. Throw on the present-but-invalid case so it reaches the caller's error path, and keep ENOENT returning null so an actually missing file still reads as absent. Both sides of that boundary are now pinned.
This commit is contained in:
@@ -68,7 +68,9 @@ export async function readGeminiCredentials(): Promise<GeminiCredentials | null>
|
||||
) {
|
||||
return parsed as GeminiCredentials
|
||||
}
|
||||
return null
|
||||
// A file that exists but does not match the credential contract is a failed
|
||||
// read. Preserve that distinction so callers do not report it as signed out.
|
||||
throw new Error('Gemini CLI credentials file is invalid')
|
||||
} catch (err) {
|
||||
if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {
|
||||
return null
|
||||
|
||||
@@ -153,6 +153,36 @@ describe('fetchGeminiRateLimits fallback oauth creds', () => {
|
||||
expect(result.weekly).toBeNull()
|
||||
})
|
||||
|
||||
it('reports a present but malformed oauth_creds.json as a failed read', async () => {
|
||||
readFileMock.mockImplementation(async (filePath: string) => {
|
||||
if (filePath.includes('auth.json')) {
|
||||
return JSON.stringify({})
|
||||
}
|
||||
if (filePath.includes('oauth_creds.json')) {
|
||||
return JSON.stringify({
|
||||
access_token: 42,
|
||||
refresh_token: 'refresh',
|
||||
expiry_date: Date.now()
|
||||
})
|
||||
}
|
||||
throw { code: 'ENOENT' }
|
||||
})
|
||||
|
||||
const result = await fetchGeminiRateLimits(true)
|
||||
|
||||
expect(result.status).toBe('error')
|
||||
expect(result.error).toContain('credentials file is invalid')
|
||||
})
|
||||
|
||||
it('keeps an actually missing oauth_creds.json as absent', async () => {
|
||||
readFileMock.mockRejectedValue({ code: 'ENOENT' })
|
||||
|
||||
const result = await fetchGeminiRateLimits(true)
|
||||
|
||||
expect(result.status).toBe('unavailable')
|
||||
expect(result.error).toContain('credentials not found')
|
||||
})
|
||||
|
||||
it('returns error when loadCodeAssist cannot resolve a project for oauth_creds path', async () => {
|
||||
// Why: when the fallback (oauth_creds.json) path has no project embedded
|
||||
// and loadCodeAssist fails, we surface a clear "project ID not found"
|
||||
|
||||
Reference in New Issue
Block a user