fix(providers): classify HTTP 400 model-unavailable as MODEL_NOT_FOUND so Antigravity Pro fallback locks out the deprecated model (#8319)

This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-24 09:36:08 -03:00
committed by GitHub
parent ddbd054e49
commit dcbea8eb0d
3 changed files with 56 additions and 2 deletions

View File

@@ -0,0 +1 @@
- fix(providers): classify HTTP 400 model-unavailable as MODEL_NOT_FOUND so Antigravity Pro fallback locks out the deprecated model

View File

@@ -232,8 +232,18 @@ export function classifyProviderError(
}
if (statusCode >= 500) return PROVIDER_ERROR_TYPES.SERVER_ERROR;
if (statusCode === 400 && isContextOverflow(bodyStr)) {
return PROVIDER_ERROR_TYPES.CONTEXT_OVERFLOW;
if (statusCode === 400) {
if (isContextOverflow(bodyStr)) {
return PROVIDER_ERROR_TYPES.CONTEXT_OVERFLOW;
}
// Some providers (e.g. Antigravity's Pro-fallback chain, #8136) return a
// plain 400 for a model that is no longer available, instead of 404/401.
// Without this check the error falls through to `return null`, so
// lockModel() never fires and the same dead model gets retried on every
// request. Detect the phrasing here, same as the 401 branch above (#7268).
if (containsModelUnavailableMessage(bodyStr)) {
return PROVIDER_ERROR_TYPES.MODEL_NOT_FOUND;
}
}
return null;

View File

@@ -0,0 +1,43 @@
import test from "node:test";
import assert from "node:assert/strict";
const { classifyProviderError, PROVIDER_ERROR_TYPES } = await import(
"../../open-sse/services/errorClassifier.ts"
);
test("#8136: classifyProviderError(400, Antigravity 'model is not supported' body) returns MODEL_NOT_FOUND", () => {
const body = {
error: {
code: 400,
message: "The model gemini-3.1-pro-low is not supported for this project.",
status: "INVALID_ARGUMENT",
},
};
const classified = classifyProviderError(400, body, "antigravity");
assert.equal(
classified,
PROVIDER_ERROR_TYPES.MODEL_NOT_FOUND,
`expected MODEL_NOT_FOUND, got ${JSON.stringify(classified)}`
);
});
test("#8136: same phrasing at 401 still works (regression guard for #7268)", () => {
const body = { error: { message: "The model gemini-3.1-pro-low is not supported for this project." } };
assert.equal(classifyProviderError(401, body, "antigravity"), PROVIDER_ERROR_TYPES.MODEL_NOT_FOUND);
});
test("#8136: plain 400 context-overflow still classifies as CONTEXT_OVERFLOW, not MODEL_NOT_FOUND", () => {
const body = {
error: {
message: "This model's maximum context length is 128000 tokens. Please reduce the length of the messages.",
},
};
assert.equal(
classifyProviderError(400, body, "antigravity"),
PROVIDER_ERROR_TYPES.CONTEXT_OVERFLOW
);
});
test("#8136: a generic 400 bad-request body (no model-unavailable phrasing) is not reclassified as MODEL_NOT_FOUND", () => {
const body = { error: { message: "Invalid request: missing required field 'messages'." } };
assert.equal(classifyProviderError(400, body, "antigravity"), null);
});