Repository navigation
fix(rate-limits): stop showing Gemini failures as Antigravity "Refresh failed" - #15876
Conversation
…h failed"
The Antigravity snapshot was synthesized as `{ ...gemini, provider: 'antigravity' }`,
so a Gemini fetch error was republished under the Antigravity provider id even though
Orca never queries Antigravity (its CLI keeps the token in the OS keyring). Derive the
snapshot instead: mirror only a successful Gemini read, otherwise report `unavailable`
with Antigravity-specific copy. This also drops Antigravity out of the error retry lane.
Fixes #14227
📝 WalkthroughWalkthroughAdds 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6e28c2b4-5377-4b38-a48c-1cad22d56513
📒 Files selected for processing (4)
src/main/rate-limits/antigravity-usage-mirror.test.tssrc/main/rate-limits/antigravity-usage-mirror.tssrc/main/rate-limits/service-antigravity-usage.test.tssrc/main/rate-limits/service.ts
Included review availability: Your plan provides up to 10 included reviews per hour; 2 remain after this review.
| export function deriveAntigravityRateLimits(gemini: ProviderRateLimits): ProviderRateLimits { | ||
| if (gemini.status === 'ok') { | ||
| return { ...gemini, provider: 'antigravity' } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Do not copy Gemini-only buckets into Antigravity state.
Line 15 copies buckets, but ProviderRateLimits defines them as Gemini-only. Construct the Antigravity result from shared quota fields. Add a regression test with Gemini buckets and assert that Antigravity has no buckets.
Proposed fix
if (gemini.status === 'ok') {
- return { ...gemini, provider: 'antigravity' }
+ return {
+ provider: 'antigravity',
+ session: gemini.session,
+ weekly: gemini.weekly,
+ updatedAt: gemini.updatedAt,
+ error: null,
+ status: 'ok',
+ usageMetadata: gemini.usageMetadata
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function deriveAntigravityRateLimits(gemini: ProviderRateLimits): ProviderRateLimits { | |
| if (gemini.status === 'ok') { | |
| return { ...gemini, provider: 'antigravity' } | |
| export function deriveAntigravityRateLimits(gemini: ProviderRateLimits): ProviderRateLimits { | |
| if (gemini.status === 'ok') { | |
| return { | |
| provider: 'antigravity', | |
| session: gemini.session, | |
| weekly: gemini.weekly, | |
| updatedAt: gemini.updatedAt, | |
| error: null, | |
| status: 'ok', | |
| usageMetadata: gemini.usageMetadata | |
| } |
…h failed" (stablyai#15876) (cherry picked from commit 636f428)
|
@nwparker Thanks for fixing the misleading “Refresh failed” state in #15876—that was a useful correctness improvement. I think it addresses the visible symptom, but not the underlying provider issue in #14227. I’m hitting this concretely: I have a Google AI plan and use More generally, mirroring a successful Gemini result can also show quota from a different account than the active Antigravity session. #14571 implements the missing native path: it queries the already-running host-local Antigravity service directly for all four quota buckets, supports Desktop and Could we treat #15876 as the interim error-state fix and #14571 as the follow-up that completes the actual provider support? I think reopening #14227—or linking a follow-up until the native fetcher lands—would better reflect the remaining gap. |
ELI5
The Antigravity chip in the status bar was showing a red "Refresh failed" for a request Orca never made. Orca doesn't actually talk to Antigravity at all — it was quietly copying whatever the Gemini usage fetch returned and relabelling it as Antigravity. So when a user's Gemini sign-in was broken, Antigravity "failed" too. Now Orca only borrows Gemini's numbers when they actually arrived; otherwise the chip says, honestly, that Antigravity usage isn't available.
What Changed
src/main/rate-limits/antigravity-usage-mirror.tswithderiveAntigravityRateLimits(gemini): mirrors the Gemini snapshot only whengemini.status === 'ok', otherwise returnsstatus: 'unavailable'with Antigravity-specific copy and no borrowed Gemini error text. The copy branches on the Gemini status so it does not misattribute the cause: a Geminiunavailable(opt-in off, or no credentials on disk) says a Gemini CLI sign-in must be connected, while a Geminierror(token refresh failed, project ID not found — the reported case, where the sign-in does exist) says the shared Code Assist quota could not be read right now. It reusesgemini.updatedAtso the window-activation freshness check isn't forced into a refetch every cycle.src/main/rate-limits/service.ts: replaced the{ ...gemini, provider: 'antigravity' }spread inrunFetchAllCyclewith a call to that function (net -5/+3 lines). ThetrackActiveFailureStreakandapplyStalePolicycalls are untouched — both already behave correctly for an ok-or-unavailable snapshot (applyStalePolicypassesunavailablethrough verbatim).No renderer change. The
unavailablerendering path already exists and is covered:StatusBar.tsx:1288renders a muted--chip,tooltip.tsx:269renders the reason as muted text,usage-roster-row-state.ts:61labels the row "Usage unavailable".Why
Root cause is not a missing fetcher registration (as the issue guessed) — it's a fake one.
runFetchAllCyclenever fetched Antigravity; it spread the Gemini result and relabelled it, so Gemini'sstatus: 'error'and Gemini's raw error string ("Token refresh failed", "Gemini project ID not found") were republished under the Antigravity provider id. The renderer has no way to distinguish that from a real failure, sogetProviderUsageStatusLabelfell through to "Refresh failed" (KO: "새로 고침 실패") — the exact reported symptom. The inverse was wrong too: a successful Gemini read was presented as Antigravity usage even for a different Google account.I chose the honest
unavailablestate over building a real Antigravity fetcher, deliberately:fetchGeminiRateLimitsreads only~/.gemini/oauth_creds.jsonand OpenCode'sauth.json(gemini-oauth-sources.ts:8,29-52). The Antigravity CLI (agy) writes neither. On a machine with the Antigravity CLI installed and signed in,~/.gemini/oauth_creds.jsondoes not exist and~/.gemini/google_accounts.jsonis{"active": null}; the CLI's own logs showChainedAuth: authenticated via keyring, and the credential is an OS keyring item (servicegemini, accountantigravity).gemini-usage-fetcher.ts:207-210already encodes exactly this caution for Gemini's own opt-in.unavailableis already the codebase's word for this state, so no new vocabulary or wire surface is introduced.No fetch-cadence change: Antigravity does drop out of
getActiveWindowRefreshPlan's retryable-failure list, butgeminiitself is still there and is likewise absent fromINDIVIDUALLY_REFRESHABLE_PROVIDERS, so the plan still falls throughcanRefreshIndividually === falseto a fullfetchAll(). (An earlier draft of this description claimed a refresh-cadence win; it was wrong and has been corrected.)Linked Issue
Fixes #14227
Visual Proof
N/A— no renderer code changed. The visible difference is produced entirely by the already-shippedunavailablebranch. A reviewer can confirm by reading the two existing render paths: with a failing Gemini sign-in the Antigravity chip previously took the error branch (StatusBar.tsx:1297, warning styling, "Refresh failed" in the tooltip) and now takes the unavailable branch (StatusBar.tsx:1288, muted--chip; tooltip shows the Antigravity reason sentence). Reproducing this live requires a Google account with no usable Code Assist project, which I could not stage.Testing
Commands run (macOS 15, arm64):
Regression proof (mirror vs.
service.ts): I stashed onlyservice.ts(leaving the new tests and the new module in place) and re-ranservice-antigravity-usage.test.ts— 2 of 3 tests failed withexpected 'error' to be 'unavailable', and the mirrored-ok test still passed, confirming the happy path is unchanged. Restoringservice.tsmade all 3 pass.Regression proof (cause-specific copy): I collapsed the
gemini.status === 'unavailable' ? ... : ...ternary inantigravity-usage-mirror.tsback to the single sign-in sentence and re-ran the unit test —does not blame a missing sign-in when the quota read itself failedfailed withexpected 'Antigravity usage is not available. O…' to contain 'could not be read right now'. Restoring the branch made all 5 pass.Platforms: tests executed on macOS only. Linux and Windows are covered by reasoning — the change is a pure function over an already-fetched snapshot, with no filesystem paths, no shell, no process spawn, and no platform branches. SSH/remote is covered by reasoning; no execution-host contact is added or removed.
I did not run
pnpm typecheck/pnpm lintlocally (they OOM in this environment); CI covers them.Review
'unavailable'is already in the shippedProviderRateLimitStatusunion (src/shared/rate-limit-types.ts:12) and in the mobile zod schema (mobile/src/components/accounts-snapshot.ts:54). This is the "changing what the host publishes reaches old clients even with no wire change" case fromdocs/reference/remote-wire-compatibility.md, and it is safe because old clients already renderunavailableas a muted not-configured chip. No capability negotiation needed.antigravitystatus-bar toggle and_antigravityStatusBarDefaultAddedkeep their meaning. Users who currently see numbers keep seeing them.--with an explanation.out/main/index.js, a build artifact; the fix belongs insrc/main/rate-limits/.Known limitations / follow-ups
Deliberately out of scope:
hasUsageProviderSettingsForProvider(status-bar-provider-visibility.ts:107) still requiresantigravityUsageConfigured && geminiCliOAuthEnabled, while the "Antigravity Usage" toggle is offered wheneveragyis on PATH. A user with Antigravity installed and Gemini CLI OAuth off gets no chip at all. Relaxing that gate also means adding the term tohasUsageProviderSettings, which would suppress the setup CTA for Antigravity-only users — a deliberate UX call, not a drive-by. The reported symptom requiresgeminiCliOAuthEnabledon, so this fix is complete for the reported case.'Gemini CLI OAuth is disabled in settings'and the MiniMax credential-error path) and rendered raw bytooltip.tsx:269, so the detail line stays untranslated on a Korean UI even though the label localizes correctly. Fixing that properly means a provider-keyed unavailable message inusage-error-copy.tsdriven byfailureKind, applied to Gemini and MiniMax at the same time — not an Antigravity one-off. Reviewer-flagged and knowingly accepted here: the label above the sentence (Usage unavailable) does localize, so a non-English UI still gets a correct status, just an English detail line — the same gap the shipped Gemini and MiniMax unavailable strings already have.Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Security, cross-platform support, remote SSH, mobile, backwards compatibility, and performance are all addressed in Review above. Mobile needs no edit: the schema already permits
unavailableand the accounts view treats it as no active usage.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)