fix(quota): improve 403 error messaging for forbidden accounts

Instead of showing "Quota access forbidden" generic error, now shows:
- Antigravity: "403 Forbidden - No Gemini Code Assist access"
- Codex: "403 Forbidden - No quota API access"

Keeps success=false with isForbidden flag so UI can show distinct
"403" badge (similar to Antigravity-Manager) rather than conflating
with 0% exhausted state.

403 ≠ 0% exhausted - they are semantically different:
- 403: Account lacks API access entirely
- 0%: Account has access but quota is used up (shows reset time)
This commit is contained in:
kaitranntt
2026-02-02 11:52:23 -05:00
parent be63056d11
commit 5a308db409
4 changed files with 11 additions and 2 deletions
+4 -1
View File
@@ -253,13 +253,16 @@ export async function fetchCodexQuota(
} }
if (response.status === 403) { if (response.status === 403) {
// 403 = account lacks API access (not same as quota exhausted)
// Keep success=false with isForbidden flag for UI to show distinct "403" badge
return { return {
success: false, success: false,
windows: [], windows: [],
planType: null, planType: null,
lastUpdated: Date.now(), lastUpdated: Date.now(),
error: 'Quota access not available (free plan may not have quota API access)', error: '403 Forbidden - No quota API access',
accountId, accountId,
isForbidden: true,
}; };
} }
+3 -1
View File
@@ -407,12 +407,14 @@ async function fetchAvailableModels(accessToken: string, _projectId: string): Pr
clearTimeout(timeoutId); clearTimeout(timeoutId);
if (response.status === 403) { if (response.status === 403) {
// 403 = account lacks Gemini Code Assist access (not same as quota exhausted)
// Keep success=false with isForbidden flag for UI to show distinct "403" badge
return { return {
success: false, success: false,
models: [], models: [],
lastUpdated: Date.now(), lastUpdated: Date.now(),
isForbidden: true, isForbidden: true,
error: 'Quota access forbidden for this account', error: '403 Forbidden - No Gemini Code Assist access',
}; };
} }
+2
View File
@@ -45,6 +45,8 @@ export interface CodexQuotaResult {
accountId?: string; accountId?: string;
/** True if token is expired and needs re-authentication */ /** True if token is expired and needs re-authentication */
needsReauth?: boolean; needsReauth?: boolean;
/** True if account lacks quota access (403) - displayed as 0% instead of error */
isForbidden?: boolean;
} }
/** /**
+2
View File
@@ -180,6 +180,8 @@ export interface CodexQuotaResult {
needsReauth?: boolean; needsReauth?: boolean;
/** True if result was served from cache */ /** True if result was served from cache */
cached?: boolean; cached?: boolean;
/** True if account lacks quota access (403) - displayed as 0% instead of error */
isForbidden?: boolean;
} }
/** Gemini CLI bucket (grouped by model series) */ /** Gemini CLI bucket (grouped by model series) */