Skip to content

fix(antigravity): rotate image accounts on explicit quota exhaustion - #9908

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.51from
Ardem2025:fix/antigravity-image-quota-account-rotation
Sep 16, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.51from
Ardem2025:fix/antigravity-image-quota-account-rotation

Conversation

@Ardem2025

Copy link
Copy Markdown
Contributor

Summary

  • Extend the existing image credential retry coordinator so Antigravity can try another account after an explicit quota-exhaustion response.
  • Keep image retries deliberately narrow: ordinary 429, generic RESOURCE_EXHAUSTED, and non-Antigravity failures remain terminal.
  • Preserve the existing cross-account retry behavior for 401 responses.

Why

OmniRoute already refreshes OAuth credentials and rotates image accounts after 401, but an Antigravity account with an explicitly exhausted image quota returns 429. The request currently stops even when another configured Antigravity account still has quota.

Image generation may be non-idempotent, so retrying every 429 would be unsafe. This change reuses the existing Antigravity 429 classifier and permits rotation only when:

  1. the provider is antigravity;
  2. the response status is 429; and
  3. classify429(error) === "quota_exhausted".

Generic rate limits, malformed/system-payload errors, and non-Antigravity responses do not rotate accounts.

Related Issues

Validation

  • Focused tests: node --import tsx/esm --test tests/unit/antigravity-image-credential-retry.test.ts
  • npm run lint
  • Production-code changes include automated tests in this PR
  • SonarQube PR analysis — pending CI

Validation was run from a clean clone on Node v22.22.3 / npm 10.9.8.

Tests Added Or Updated

  • tests/unit/antigravity-image-credential-retry.test.ts
    • rotates for an explicit Antigravity Individual quota reached response;
    • rejects a generic RESOURCE_EXHAUSTED response;
    • rejects an ordinary image rate-limit response;
    • rejects quota-shaped responses from non-Antigravity providers.

Coverage Notes

The new focused unit file directly covers the exported retry predicate and its positive and negative classification boundaries. The retry coordinator uses the same predicate to decide whether another credential may be selected.

Reviewer Notes

  • No schema, migration, settings, or API contract changes.
  • The retry remains bounded by the coordinator's existing tried-connection set and maximum-attempt limit.
  • The intentionally narrow classification avoids replaying image requests for ambiguous or provider-wide rate limits.

@diegosouzapw

Copy link
Copy Markdown
Owner

Clean, well-scoped fix — reusing the existing classify429 classifier and keeping the
rotation strictly to Antigravity's explicit quota-exhausted signal (not generic 429s) is the
right level of caution for a non-idempotent operation like image generation. Confirmed on the
current tip: imageCredentialRetry.ts still only rotates on 401/retryable, so this gap is
real. Probe run: all 4 of your tests pass at your PR head. Before merge: retarget to
release/v3.8.51, add a changelog fragment, and there's a small comment-only conflict to
resolve (an unrelated Gemini Web retirement changed a nearby comment's issue reference) — no
logic conflict. Planning to treat this as the winner over your earlier #8053 attempt on the
same goal, since this version is smaller, cleaner, and already mergeable.

@diegosouzapw
diegosouzapw changed the base branch from release/v3.8.50 to release/v3.8.51 September 15, 2026 11:30
Ardem2025 and others added 2 commits September 17, 2026 00:23
…ation

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
@Ardem2025
Ardem2025 force-pushed the fix/antigravity-image-quota-account-rotation branch from e2c9bec to 58bd92e Compare September 16, 2026 21:23
diegosouzapw pushed a commit that referenced this pull request Sep 16, 2026
Merged after batch validation on a combined worktree cut from `release/v3.8.51` with #9908 and #7138.

**Evidence**
- Focused tests: 36/36 pass on the combined tree (`oauth-modal-codex-lan-ip-8046`, `antigravity-image-credential-retry`, `antigravity-usage-service`, `generic-quota-fetcher`), including the 2 pre-existing anchor tests that assert `codex` stays in `PKCE_CALLBACK_SERVER_PROVIDERS` and that the `localhost:1455` redirect URI is untouched.
- Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS.
- `check-file-size` is red, but reproduces byte-identical on the pure `release/v3.8.51` tip (`imageGeneration.ts`, `roundRobinCombo.ts`, `stream.ts`) — inherited base-red, not from this PR.

**Reconciled**
- `changelog.d/fixes/codex-manual-loopback-action.md` did not start with a markdown bullet, which is the one thing `check-changelog-integrity` failed on. Fixed in your branch (d3dbb5b) so the fragment convention holds; nothing else in your diff was touched.

Thanks for this one, @Ardem2025 — exposing the manual callback entry that already existed in the code instead of adding a new flow is exactly the right shape for the LAN/remote case, and keeping every PKCE/state check untouched made it easy to verify.
@diegosouzapw
diegosouzapw merged commit 00860f3 into diegosouzapw:release/v3.8.51 Sep 16, 2026
11 of 16 checks passed
diegosouzapw pushed a commit that referenced this pull request Sep 16, 2026
Merged after batch validation on a combined worktree cut from `release/v3.8.51` with #9944 and #9908.

**Evidence**
- Focused tests: 36/36 pass on the combined tree, including `convertUsageToQuotaInfo skips Antigravity quota entries with an unknown fraction` and the `#6295` regression that guards the same class of bug on another provider.
- Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS.
- The red `check-file-size` reproduces byte-identical on the pure `release/v3.8.51` tip — inherited base-red, not from this PR. The red CI run here dates from 2026-09-15 against an older base.

Thanks, @Ardem2025 — this is the smallest diff of your batch and arguably the one with the widest blast radius avoided. Writing `remainingPercentage: 0` for an unreported fraction made "we don't know" numerically indistinguishable from "fully exhausted" to every downstream consumer of the quota cache; omitting the field so preflight fails open is the correct read of the upstream's silence.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
Merged after batch validation on a combined worktree cut from `release/v3.8.51` with diegosouzapw#9908 and diegosouzapw#7138.

**Evidence**
- Focused tests: 36/36 pass on the combined tree (`oauth-modal-codex-lan-ip-8046`, `antigravity-image-credential-retry`, `antigravity-usage-service`, `generic-quota-fetcher`), including the 2 pre-existing anchor tests that assert `codex` stays in `PKCE_CALLBACK_SERVER_PROVIDERS` and that the `localhost:1455` redirect URI is untouched.
- Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS.
- `check-file-size` is red, but reproduces byte-identical on the pure `release/v3.8.51` tip (`imageGeneration.ts`, `roundRobinCombo.ts`, `stream.ts`) — inherited base-red, not from this PR.

**Reconciled**
- `changelog.d/fixes/codex-manual-loopback-action.md` did not start with a markdown bullet, which is the one thing `check-changelog-integrity` failed on. Fixed in your branch (d3dbb5b) so the fragment convention holds; nothing else in your diff was touched.

Thanks for this one, @Ardem2025 — exposing the manual callback entry that already existed in the code instead of adding a new flow is exactly the right shape for the LAN/remote case, and keeping every PKCE/state check untouched made it easy to verify.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…iegosouzapw#9908)

Merged after batch validation on a combined worktree cut from `release/v3.8.51` with diegosouzapw#9944 and diegosouzapw#7138.

**Evidence**
- Focused tests: 36/36 pass on the combined tree, including your 4 classification-boundary cases in `tests/unit/antigravity-image-credential-retry.test.ts` (Antigravity quota-exhausted `429` rotates; generic `RESOURCE_EXHAUSTED`, ordinary image rate-limit and non-Antigravity `429` stay terminal).
- Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS.
- The red `check-file-size` reproduces byte-identical on the pure `release/v3.8.51` tip — inherited base-red, not from this PR. The red CI run on this PR dates from 2026-09-15 against an older base.

**Related dispositions**
- diegosouzapw#8053 is being closed in your favour: it chased the same account-rotation goal across 3 files plus a new `routingInstrumentation.ts`, while its `AbortSignal` half was already superseded on the tip by independent work. This PR does the same job in 31 lines of production code by reusing the existing `classify429` engine.

Thanks, @Ardem2025 — the deliberate narrowness here is the reason this merged and the bigger version didn't. Gating rotation on `provider === "antigravity" && status === 429 && classify429() === "quota_exhausted"` keeps non-idempotent image generation from being retried on ordinary rate limits, and you proved each negative case rather than just the happy path.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…w#7138)

Merged after batch validation on a combined worktree cut from `release/v3.8.51` with diegosouzapw#9944 and diegosouzapw#9908.

**Evidence**
- Focused tests: 36/36 pass on the combined tree, including `convertUsageToQuotaInfo skips Antigravity quota entries with an unknown fraction` and the `diegosouzapw#6295` regression that guards the same class of bug on another provider.
- Gates on the combined tree: `check-complexity` PASS, `check-cognitive-complexity` PASS, `typecheck:core` PASS, `check-changelog-integrity` PASS.
- The red `check-file-size` reproduces byte-identical on the pure `release/v3.8.51` tip — inherited base-red, not from this PR. The red CI run here dates from 2026-09-15 against an older base.

Thanks, @Ardem2025 — this is the smallest diff of your batch and arguably the one with the widest blast radius avoided. Writing `remainingPercentage: 0` for an unreported fraction made "we don't know" numerically indistinguishable from "fully exhausted" to every downstream consumer of the quota cache; omitting the field so preflight fails open is the correct read of the upstream's silence.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants