fix(model): return clear error instead of silent openai default for unrecognized models (#2492)

Integrated into release/v3.8.2
This commit is contained in:
Hernan Javier Ardila Sanchez
2026-05-21 20:52:28 +02:00
committed by GitHub
parent 7be33531b2
commit 8d43cbd53c
4 changed files with 38 additions and 7 deletions

View File

@@ -468,11 +468,17 @@ async function resolveModelByProviderInference(modelId: string, extendedContext:
return { provider: "gemini", model: modelId, extendedContext };
}
// Last resort: treat as openai model
// Last resort: no provider could be inferred — return a clear error instead
// of silently defaulting to "openai", which would produce a misleading
// "No credentials for provider: openai" response when the model name
// is unrecognised (e.g. a missing combo, a typo, or a bare model id
// that doesn't exist in any provider's catalog).
return {
provider: "openai",
provider: null,
model: modelId,
extendedContext,
errorType: "model_not_found",
errorMessage: `Unable to determine provider for model '${modelId}'. Use a provider/model prefix (e.g. openai/${modelId}) or ensure the model is added as a combo entry.`,
};
}

View File

@@ -175,6 +175,17 @@ export async function resolveModelOrError(
}
if (!modelInfo.provider) {
// model_not_found: raised by resolveModelByProviderInference when no
// provider could be inferred — return a clear error instead of the
// misleading "openai" default that the old code silently fell back to.
if ((modelInfo as any).errorType === "model_not_found") {
const message =
(modelInfo as any).errorMessage ||
`Model '${modelStr}' could not be resolved to a known provider.`;
log.warn("CHAT", message, { model: modelStr });
return { error: errorResponse(HTTP_STATUS.BAD_REQUEST, message) };
}
if ((modelInfo as any).errorType === "ambiguous_model") {
// Family disambiguation: if the model name begins with a known
// non-OAuth family prefix, auto-pick the family-native provider

View File

@@ -387,3 +387,17 @@ test("withSessionHeader adds headers to mutable and immutable responses", async
assert.equal(immutable.status, 302);
assert.equal(await immutable.text(), "");
});
test("resolveModelOrError returns model_not_found error for unrecognised bare model names", async () => {
const result = await resolveModelOrError(
"completely-unknown-model-xyz",
{ messages: [{ role: "user", content: "hello" }] },
"/v1/chat/completions"
);
assert.ok(result.error);
assert.equal(result.error.status, 400);
const json = (await result.error.json()) as any;
assert.match(json.error.message, /Unable to determine provider/i);
assert.match(json.error.message, /completely-unknown-model-xyz/i);
});

View File

@@ -354,11 +354,11 @@ test("model helpers cover malformed input, alias maps, wildcard aliases, ambigui
model: "gemini-custom",
extendedContext: false,
});
assert.deepEqual(await modelService.getModelInfoCore("made-up-model", {}), {
provider: "openai",
model: "made-up-model",
extendedContext: false,
});
const unknownModel = await modelService.getModelInfoCore("made-up-model", {});
assert.equal(unknownModel.provider, null);
assert.equal(unknownModel.errorType, "model_not_found");
assert.ok(unknownModel.errorMessage.includes("made-up-model"));
assert.ok(unknownModel.errorMessage.includes("provider/model prefix"));
});
test("error classifier covers empty-content helpers, context overflow and remaining error classes", () => {