From 8d43cbd53cf94570baf67c6d1e7518af74adef86 Mon Sep 17 00:00:00 2001 From: Hernan Javier Ardila Sanchez Date: Thu, 21 May 2026 20:52:28 +0200 Subject: [PATCH] fix(model): return clear error instead of silent openai default for unrecognized models (#2492) Integrated into release/v3.8.2 --- open-sse/services/model.ts | 10 ++++++++-- src/sse/handlers/chatHelpers.ts | 11 +++++++++++ tests/unit/chat-helpers.test.ts | 14 ++++++++++++++ tests/unit/services-branch-hardening.test.ts | 10 +++++----- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/open-sse/services/model.ts b/open-sse/services/model.ts index 08c4aefdc1..eff48b209c 100644 --- a/open-sse/services/model.ts +++ b/open-sse/services/model.ts @@ -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.`, }; } diff --git a/src/sse/handlers/chatHelpers.ts b/src/sse/handlers/chatHelpers.ts index ff42f01a07..658e4a4cb8 100644 --- a/src/sse/handlers/chatHelpers.ts +++ b/src/sse/handlers/chatHelpers.ts @@ -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 diff --git a/tests/unit/chat-helpers.test.ts b/tests/unit/chat-helpers.test.ts index 1c1603bf61..7fa4d007c3 100644 --- a/tests/unit/chat-helpers.test.ts +++ b/tests/unit/chat-helpers.test.ts @@ -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); +}); diff --git a/tests/unit/services-branch-hardening.test.ts b/tests/unit/services-branch-hardening.test.ts index 30d320e764..4e90bb1a3a 100644 --- a/tests/unit/services-branch-hardening.test.ts +++ b/tests/unit/services-branch-hardening.test.ts @@ -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", () => {