mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 14:52:09 +03:00
fix(providers): treat recoverable Antigravity/Cloud-Code 403s as project errors, not account bans (#6452)
fix(providers): treat recoverable Antigravity/CF 403 as retryable (net +1/-0, test OK). Integrated into release/v3.8.46.
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(providers):** recoverable Antigravity / Cloudflare `403` responses are now classified as retryable instead of terminal, so a transient WAF block no longer bans the connection. Regression guard: `tests/unit/errorclassifier-antigravity-403.test.ts`. (thanks @developerjillur)
|
||||
- **fix(mitm):** `sanitizeHeaders` now redacts `Set-Cookie` response headers so upstream session cookies never leak into logs / diagnostics. Regression guard: `tests/unit/mitm-sanitize-headers.test.ts`. (thanks @developerjillur)
|
||||
- **fix(api):** `/api/compression/preview` now accepts `mode: "caveman"` and correctly handles stacked / zero-compression previews ([#6425](https://github.com/diegosouzapw/OmniRoute/issues/6425)). Regression guard: `tests/unit/api/compression-preview-caveman-and-stacked-6425.test.ts`. (thanks @chirag127)
|
||||
- **feat(providers):** add **Zed** hosted LLM aggregator as a native-app provider ([#6118](https://github.com/diegosouzapw/OmniRoute/pull/6118)) — OAuth sign-in via the Zed hosted flow, registered through the shared provider registry + executor. Regression guards: `tests/unit/zed-oauth-provider.test.ts`, `zed-import-utils.test.ts`, `zed-docker-detect.test.ts`, `mitm-handler-zed.test.ts`. VPS-validated via live operator login (Hard Rule #18).
|
||||
|
||||
@@ -158,7 +158,29 @@ export function classifyProviderError(
|
||||
return PROVIDER_ERROR_TYPES.ACCOUNT_DEACTIVATED;
|
||||
}
|
||||
if (statusCode === 403) {
|
||||
if (bodyStr.includes("has not been used in project")) {
|
||||
// Cloud Code / Antigravity (Gemini Code Assist) 403s are almost always a
|
||||
// RECOVERABLE project-config issue — the Cloud AI Companion API not enabled
|
||||
// on the project ("has not been used in project …", SERVICE_DISABLED,
|
||||
// accessNotConfigured), a stale/mismatched project, or PERMISSION_DENIED on
|
||||
// the project — NOT an account ban. Real account bans are already caught by
|
||||
// isAccountDeactivated above (→ ACCOUNT_DEACTIVATED). Classifying these as
|
||||
// PROJECT_ROUTE_ERROR keeps the account active and recoverable once the
|
||||
// project/API is fixed, instead of permanently disabling it on a single
|
||||
// fixable 403 (which previously required a full OAuth reconnect). (antigravity-403)
|
||||
const p = (provider || "").toLowerCase();
|
||||
const isCloudCodeProvider =
|
||||
p === "antigravity" ||
|
||||
p === "gemini-cli" ||
|
||||
p.includes("cloudcode") ||
|
||||
p.includes("cloud-code");
|
||||
const recoverableProject403 =
|
||||
bodyStr.includes("has not been used in project") ||
|
||||
bodyStr.includes("SERVICE_DISABLED") ||
|
||||
bodyStr.includes("accessNotConfigured") ||
|
||||
bodyStr.includes("PERMISSION_DENIED") ||
|
||||
/\bit is disabled\b/i.test(bodyStr) ||
|
||||
isCloudCodeProvider;
|
||||
if (recoverableProject403) {
|
||||
return PROVIDER_ERROR_TYPES.PROJECT_ROUTE_ERROR;
|
||||
}
|
||||
if (provider && getProviderCategory(provider) === "apikey") {
|
||||
|
||||
50
tests/unit/errorclassifier-antigravity-403.test.ts
Normal file
50
tests/unit/errorclassifier-antigravity-403.test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
classifyProviderError,
|
||||
PROVIDER_ERROR_TYPES,
|
||||
} from "../../open-sse/services/errorClassifier.ts";
|
||||
|
||||
// A Cloud Code / Antigravity (Gemini Code Assist) 403 is almost always a
|
||||
// RECOVERABLE project-config problem — the Cloud AI Companion API not enabled on
|
||||
// the project, a stale project, or PERMISSION_DENIED — NOT an account ban.
|
||||
// It must classify as PROJECT_ROUTE_ERROR so the account stays usable once the
|
||||
// project is fixed, instead of being disabled for ~a year like a real ban.
|
||||
|
||||
test("403 'has not been used in project' (antigravity) -> PROJECT_ROUTE_ERROR", () => {
|
||||
const body = {
|
||||
error: {
|
||||
code: 403,
|
||||
status: "PERMISSION_DENIED",
|
||||
message:
|
||||
"Cloud AI Companion API has not been used in project 123 before or it is disabled.",
|
||||
},
|
||||
};
|
||||
assert.equal(
|
||||
classifyProviderError(403, body, "antigravity"),
|
||||
PROVIDER_ERROR_TYPES.PROJECT_ROUTE_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
test("403 SERVICE_DISABLED / PERMISSION_DENIED (gemini-cli) -> PROJECT_ROUTE_ERROR", () => {
|
||||
const body = { error: { status: "PERMISSION_DENIED", details: [{ reason: "SERVICE_DISABLED" }] } };
|
||||
assert.equal(
|
||||
classifyProviderError(403, body, "gemini-cli"),
|
||||
PROVIDER_ERROR_TYPES.PROJECT_ROUTE_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
test("403 on a cloud-code provider with a bare body -> still recoverable PROJECT_ROUTE_ERROR", () => {
|
||||
assert.equal(
|
||||
classifyProviderError(403, "forbidden", "antigravity-cloudcode"),
|
||||
PROVIDER_ERROR_TYPES.PROJECT_ROUTE_ERROR,
|
||||
);
|
||||
});
|
||||
|
||||
test("403 real ban signal -> still ACCOUNT_DEACTIVATED (ban detection preserved)", () => {
|
||||
const body = "This service has been disabled in this account for violation of policy.";
|
||||
assert.equal(
|
||||
classifyProviderError(403, body, "antigravity"),
|
||||
PROVIDER_ERROR_TYPES.ACCOUNT_DEACTIVATED,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user