{"record":{"id":"d57d0478198d5f51","repo":"router-for-me/CLIProxyAPI","slug":"model-not-found","errorCode":"model_not_found","errorMessage":"unknown provider for model <modelName>","messagePattern":"unknown provider for model <modelName>","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"sdk/api/handlers/handlers_routing.go","lineNumber":217,"sourceCode":"\t\tproviders = util.GetProviderName(resolvedModelName)\n\t}\n\n\tif len(providers) == 0 {\n\t\t// The client asked for a model this proxy cannot route. Report it as a request\n\t\t// error so streaming clients receive an actionable message instead of a\n\t\t// gateway failure they would keep retrying. 400 is used rather than 404 to keep\n\t\t// it distinguishable from an unregistered HTTP route.\n\t\t// The model name is client supplied, so it is inserted through sjson rather\n\t\t// than formatted into the JSON literal: an unescaped quote would otherwise\n\t\t// corrupt the body or let the caller overwrite the error code.\n\t\tbody := `{\"error\":{\"message\":\"\",\"type\":\"invalid_request_error\",\"code\":\"model_not_found\",\"param\":\"model\"}}`\n\t\tbody, errSet := sjson.Set(body, \"error.message\", \"unknown provider for model \"+modelName)\n\t\tif errSet != nil {\n\t\t\tbody = `{\"error\":{\"message\":\"unknown provider for model\",\"type\":\"invalid_request_error\",\"code\":\"model_not_found\",\"param\":\"model\"}}`\n\t\t}\n\t\treturn nil, \"\", &interfaces.ErrorMessage{\n\t\t\tStatusCode: http.StatusBadRequest,\n\t\t\tError:      errors.New(body),\n\t\t}\n\t}\n\n\t// The thinking suffix is preserved in the model name itself, so no\n\t// metadata-based configuration passing is needed.\n\treturn providers, resolvedModelName, nil\n}\n\nfunc (h *BaseAPIHandler) validateImageOnlyModel(modelName string, allowImageModel bool) *interfaces.ErrorMessage {\n\tbaseModel := strings.TrimSpace(thinking.ParseSuffix(modelName).ModelName)\n\tif baseModel == \"\" {\n\t\tbaseModel = strings.TrimSpace(modelName)\n\t}\n\tif isOpenAIImageOnlyModel(baseModel) && !allowImageModel {\n\t\treturn &interfaces.ErrorMessage{\n\t\t\tStatusCode: http.StatusServiceUnavailable,\n\t\t\tError:      fmt.Errorf(\"model %s is only supported on /v1/images/generations and /v1/images/edits\", routeModelBaseName(baseModel)),\n\t\t}","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/sdk/api/handlers/handlers_routing.go#L199-L235","documentation":"Routing error from the base API handler: after resolving the requested model name (including any thinking suffix stripped), no provider in the registry is registered to serve it, so the request cannot be dispatched. The handler deliberately returns HTTP 400 with OpenAI-style code model_not_found (rather than 404) to distinguish 'route missing' from 'model unknown', and builds the message via sjson because the model name is client-supplied and must not corrupt or inject into the JSON body.","triggerScenarios":"POST /v1/chat/completions (or any protocol route) with a model field that matches no registered provider entry — unknown name, typo, model requiring an auth/provider that is not configured or has no available credentials, or a model removed after a registry update.","commonSituations":"Model registry updated and a model renamed/removed; provider auths missing or all exhausted so the model resolves to no provider; client hardcodes a model name from a different deployment; thinking-suffix typo making the base name unmatched.","solutions":["List available models via the models endpoint and use an exact registered name","Verify the provider that should serve the model has valid auth configured (auths/ directory or management API) and is not disabled","If the model should exist, check the registry/updater state — run with --local-model off or refresh the model list","Fix typos including thinking suffixes (e.g. model:high vs model-high conventions)"],"exampleFix":"// before\ncurl -X POST http://localhost:8000/v1/chat/completions -d '{\"model\":\"gpt-5-turbo\",\"messages\":[...]}'\n// after (use a model the registry actually serves)\ncurl http://localhost:8000/v1/models\ncurl -X POST http://localhost:8000/v1/chat/completions -d '{\"model\":\"gpt-5\",\"messages\":[...]}'","handlingStrategy":"validation","validationCode":"// Validate the model name against the served list before sending the request\nmodels, _ := client.Models(ctx)\nserved := map[string]bool{}\nfor _, m := range models { served[m.ID] = true }\nif !served[req.Model] {\n    return fmt.Errorf(\"model %q is not served; pick from %d registered models\", req.Model, len(served))\n}","typeGuard":"func isServedModel(want string, served []string) bool {\n    want = strings.TrimSpace(want)\n    for _, m := range served {\n        if m == want {\n            return true\n        }\n    }\n    return false\n}","tryCatchPattern":"resp, err := client.Do(req)\nif err == nil && resp.StatusCode == http.StatusBadRequest {\n    var e struct{ Error struct{ Code, Message string } }\n    _ = json.NewDecoder(resp.Body).Decode(&e)\n    if e.Error.Code == \"model_not_found\" {\n        return fmt.Errorf(\"model %q unknown to this gateway; fetch /v1/models\", model)\n    }\n}\n// note: 400 (not 404) signals unknown model vs unknown route","preventionTips":["Fetch the models endpoint at client startup and validate all hardcoded model names","Keep provider auths healthy — models with zero available credentials drop out of routing","After registry updates, re-validate model lists in config and clients"],"tags":["routing","model-not-found","api","providers","http-400"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}