Skip to content

fix(providers): a Google Cloud Code / Antigravity 403 VALIDATION_REQUIRED no longer permanently bans the connection - #14848

Merged
diegosouzapw merged 4 commits into
cheaper-inference:release/v3.8.51from
adevwithpurpose:fix/antigravity-verify-account-403-not-a-ban
Sep 29, 2026
Merged

diegosouzapw merged 4 commits into
cheaper-inference:release/v3.8.51from
adevwithpurpose:fix/antigravity-verify-account-403-not-a-ban

Conversation

@adevwithpurpose

Copy link
Copy Markdown
Contributor

Summary

A Google Cloud Code / Antigravity 403 VALIDATION_REQUIRED ("Verify your account to
continue") no longer permanently bans the connection.

verify your account to continue was listed in ACCOUNT_DEACTIVATED_SIGNALS, so a single
transient occurrence parked a healthy, fully-quota'd account in the terminal banned state
(permanent: true, 1-year cooldown, never auto-recovers). Measured on a live deployment:
one Antigravity connection returned 33 of these 403s inside 10 minutes and stayed active,
while a sibling connection holding 100 % of its quota on all 17 windows was banned by exactly
one — the only difference being which attempt happened to be served.

There is a second, compounding defect. accountDeactivated is evaluated before the
cloud-code 403 branch in classifyProviderError, so keeping this phrase in the ban list also
made that recoverable branch unreachable for this wording — the project-route recovery added
for Gemini Code Assist could never run for it.

The phrase now lives in a new non-terminal ACCOUNT_VERIFICATION_REQUIRED_SIGNALS table and
classifies as recoverable PROJECT_ROUTE_ERROR. It is checked after accountDeactivated
so a body carrying a real ban phrase still wins, and before the generic 403 → FORBIDDEN
fall-through so a non-cloud-code provider is not banned on its first occurrence.
classifyFakeSuccessBody follows the same rule, otherwise the phrase would now fall through
to null there since it left ACCOUNT_DEACTIVATED_SIGNALS.

The table sits in errorClassifier.ts rather than accountFallback.ts because the latter is
frozen at 2517 lines (shrink-only) by config/quality/file-size-baseline.json.

Related Issues

Validation

  • Change type: provider
  • Focused tests and category gates — 141/141 across the 8 ban/403 suites
  • npm run lint — not run in full; eslint on the changed files passes clean
  • Reconciled with the current active release base — cut from upstream/release/v3.8.51
  • Production-code changes include a new or updated automated test in this PR
  • Also green: typecheck:core exit 0, check-file-size 0, check-complexity 0,
    check:changelog-integrity OK (against the correct base — see Reviewer Notes)

Tests Added Or Updated

  • Added: tests/unit/antigravity-verify-account-403-not-a-ban.test.ts (10 cases)

Coverage Notes

Changes open-sse/services/accountFallback.ts and open-sse/services/errorClassifier.ts,
covered by the new suite above plus the existing
tests/unit/errorclassifier-antigravity-403.test.ts and
tests/unit/account-fallback-service.test.ts.

The new suite is proved to fail pre-fix by mutation: restoring the phrase to
ACCOUNT_DEACTIVATED_SIGNALS produces 6 failures / 4 passes, and the 4 that stay green
are deliberately the real-ban regression guards — i.e. the suite fails on the fix's own
assertions while continuing to assert that genuine Antigravity ban wording
(this service has been disabled in this account for violation) still terminalizes, and that
a body carrying both still bans.

Reviewer Notes

1 — check:changelog-integrity and the base ref. Locally this gate appears to fail with
"11 bullet(s) present in origin/release/v3.8.50-local-unified are MISSING". That is an
artifact of resolveBaseRef(), whose local fallback picks the highest origin/release/v*.
My clone's origin is a fork whose newest release branch is a different lineage carrying
fork-only features (theoldllm, absent from this tree), so those 11 bullets were never in
this branch. Against the ref this branch was actually cut from:

CHANGELOG_BASE_REF=upstream/release/v3.8.51 npm run check:changelog-integrity
[changelog-integrity] OK — no base bullets lost vs upstream/release/v3.8.51.

No reconciliation record was added, because nothing was eaten.

2 — i18n mirrors are left stale, deliberately. docs/security/BAN_DETECTION.md is updated
but its 66 locale mirrors are not regenerated: no translation backend is configured here
(OMNIROUTE_TRANSLATION_API_URL; the local gateway answers 401). npm run i18n:check will
therefore report this doc as drifted. Note that upstream/release/v3.8.51 already ships 8
drifted core-set docs (COMPRESSION_GUIDE, AGENTBRIDGE, DOCKER_GUIDE, ENVIRONMENT,
FEATURE_FLAGS, FREE_TIERS, AUTO-COMBO, ROUTE_GUARD_TIERS), so this follows existing practice
rather than introducing it. Refresh with:

npm run i18n:run -- --files=docs/security/BAN_DETECTION.md

(Passing --files= scopes it to exactly the 66 BAN_DETECTION mirrors — verified by dry run.
Hand-editing .i18n-state.json was rejected: it is the exact false-PASS anti-pattern
documented at check-translation-drift.mjs:30-34.)

3 — Behavioural delta worth a second pair of eyes. A provider that returns this wording
now gets a recoverable error and stays in rotation instead of being parked for a year. That is
the intent, but it does mean such a connection will be retried rather than sidelined; if
upstream would rather it be retried with a backoff instead, that is a small follow-up.

…IRED no longer permanently bans the connection

"verify your account to continue" was listed in ACCOUNT_DEACTIVATED_SIGNALS, so a
single transient occurrence parked a healthy, fully-quota'd account in the terminal
`banned` state (permanent: true, 1-year cooldown, never auto-recovers). Measured on a
live deployment: one Antigravity connection returned 33 of these 403s inside 10 minutes
and stayed `active`, while a sibling holding 100 % of its quota on all 17 windows was
banned by exactly one — the only difference being which attempt happened to be served.

Because `accountDeactivated` is evaluated before the cloud-code 403 branch, keeping the
phrase there also made that recoverable branch unreachable for this wording, so the
project-route recovery added for Gemini Code Assist (cheaper-inference#868, cheaper-inference#6452) could never run for
it. cheaper-inference#14282 hit the same phrase and declared it out of scope.

The phrase now lives in a new non-terminal ACCOUNT_VERIFICATION_REQUIRED_SIGNALS table
and classifies as recoverable PROJECT_ROUTE_ERROR, checked after accountDeactivated so a
real ban phrase still wins, and before the generic 403 -> FORBIDDEN fall-through so a
non-cloud-code provider is not banned on its first occurrence. classifyFakeSuccessBody
follows the same rule. The table sits in errorClassifier.ts rather than
accountFallback.ts because the latter is frozen at 2517 lines by
config/quality/file-size-baseline.json.

Genuine Antigravity ban wording still terminalizes, and a body carrying both still bans
(regression-guarded in tests).
@adevwithpurpose

Copy link
Copy Markdown
Contributor Author

Real-world confirmation of the premise, from the affected deployment.

The Antigravity account this PR is about was banned by a single 403 VALIDATION_REQUIRED while holding 100 % of its quota on all 17 windows. After this fix was deployed, the operator signed in again through the official Antigravity app, completed the verification prompt, and the account resumed serving traffic normally — no new credentials, no new account, no support ticket.

That is the direct test of the claim this PR makes: the condition is operator-actionable and recoverable, not a dead account. A genuinely ToS-disabled account cannot be recovered by verifying in a browser. Under the old behaviour that same account would have stayed parked for a year (permanent: true) for a prompt the operator could clear in a minute.

Post-fix state on that deployment: 40 connections, all active, zero banned, including the previously banned connection after re-auth.

Operator note (may be worth documenting): a terminal banned state is not cleared by the dashboard Clear cooldown button — that only nulls rateLimitedUntil. Per RESILIENCE_GUIDE.md:107 terminal states persist until credentials change, and the clearing path is resetConnectionBackoff(), invoked from src/sse/services/auth.ts:1525 — i.e. a successful re-auth/token refresh. Easy to lose time on if you reach for the wrong button, and it is what makes this bug expensive rather than merely annoying.

Happy to help land this however is most useful:

  • move the new table somewhere else if errorClassifier.ts is the wrong home (it is there only because accountFallback.ts is frozen at 2517 lines by config/quality/file-size-baseline.json);
  • narrow or widen the branch ordering — it is deliberately after accountDeactivated and before the generic 403 → FORBIDDEN;
  • add whatever extra coverage you want; the current suite is mutation-proved (6 fail / 4 pass with the fix disabled, where the 4 are the real-ban guards);
  • regenerate the 66 BAN_DETECTION.md locale mirrors if someone can point me at a working translation backend — I have none configured locally, which is why they are stale in this PR.

@diegosouzapw
diegosouzapw merged commit 3a2678f into cheaper-inference:release/v3.8.51 Sep 29, 2026
7 checks passed
diegosouzapw added a commit that referenced this pull request Sep 29, 2026
…ge wave (#15052)

Base-red drain after the 2026-09-28/29 release-drain wave: one production defect (compression preview fuzzy toggle was a no-op since #14983), three intentional contract changes propagated (#14848, #13548, #13487 inventory site), two cross-test state leaks fixed with a refusal-memory reset. Each red reproduced on the tip and green with the fix.
Witroch4 pushed a commit to Witroch4/OmniRoute that referenced this pull request Oct 24, 2026
…IRED no longer permanently bans the connection

Port of upstream 3a2678f (cheaper-inference#14848).

"Verify your account to continue" (Cloud Code 403 VALIDATION_REQUIRED) was in
ACCOUNT_DEACTIVATED_SIGNALS, so a single occurrence parked the connection as
`banned` with a 1-year permanent cooldown. It is a transient, operator-actionable
prompt and now classifies as the recoverable PROJECT_ROUTE_ERROR.

Fork adaptation: upstream's errorClassifier conflict hunks also carried features
the fork does not have (geo-block, Cloudflare 1010/challenge, Anthropic/Grok 403
refusals, fake-success body classifier); only the verification signal and its 403
branch are ported. The test's end-to-end cases use the fork's
resolveTerminalConnectionStatus in src/sse/services/auth.ts (now exported) instead
of upstream's authTerminalStatus.ts module.

(cherry picked from commit 3a2678f)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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