{"record":{"id":"45d1cc0ba34aef72","repo":"sipeed/picoclaw","slug":"model-q-cannot-be-used-as-the-default-chat-model","errorCode":null,"errorMessage":"Model %q cannot be used as the default chat model","messagePattern":"Model %q cannot be used as the default chat model","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"web/backend/api/models.go","lineNumber":582,"sourceCode":"\tfor _, m := range cfg.ModelList {\n\t\tif m.ModelName == req.ModelName {\n\t\t\tfound = true\n\t\t\tisVirtual = m.IsVirtual()\n\t\t\tbreak\n\t\t}\n\t}\n\tif !found {\n\t\thttp.Error(w, fmt.Sprintf(\"Model %q not found in model_list\", req.ModelName), http.StatusNotFound)\n\t\treturn\n\t}\n\tif isVirtual {\n\t\thttp.Error(w, fmt.Sprintf(\"Cannot set virtual model %q as default\", req.ModelName), http.StatusBadRequest)\n\t\treturn\n\t}\n\tfor _, m := range cfg.ModelList {\n\t\tif m.ModelName == req.ModelName {\n\t\t\tif !defaultModelAllowedForModelConfig(m) {\n\t\t\t\thttp.Error(\n\t\t\t\t\tw,\n\t\t\t\t\tfmt.Sprintf(\"Model %q cannot be used as the default chat model\", req.ModelName),\n\t\t\t\t\thttp.StatusBadRequest,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\tcfg.Agents.Defaults.ModelName = req.ModelName\n\n\tif err := config.SaveConfig(h.configPath, cfg); err != nil {\n\t\thttp.Error(w, fmt.Sprintf(\"Failed to save config: %v\", err), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")","sourceCodeStart":564,"sourceCodeEnd":600,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/web/backend/api/models.go#L564-L600","documentation":"Returned by POST /api/models/default when the model is real and non-virtual but its provider fails defaultModelAllowedForModelConfig, which extracts the provider protocol and checks providers.IsDefaultModelProvider. Only chat-capable providers qualify; utility providers (embedding, TTS, ASR, etc.) are rejected because the default drives agent chat, and a non-chat model would break every new conversation.","triggerScenarios":"POST /api/models/default naming a model whose provider is, for example, an embedding or speech provider rather than an LLM chat provider. The model exists and is not virtual, but ExtractProtocol yields a provider outside the default-model allowlist.","commonSituations":"The config mixes ASR/embedding models into model_list and a script picks the first entry; a user assumes any configured model can be the default; provider renaming moved a chat provider out of the allowlist after an upgrade.","solutions":["Choose a model backed by a chat-capable provider (e.g. an OpenAI/Anthropic-compatible LLM) as the default","List models and pick one whose provider is in the default-eligible set exposed by the models endpoint","If you believe the provider should qualify, check IsDefaultModelProvider in the providers package and upgrade or patch the allowlist"],"exampleFix":"// before\nconst target = models[0]; // e.g. an embedding model\n\n// after\nconst target = models.find(m => m.provider === 'openai');","handlingStrategy":"validation","validationCode":"const models = await (await fetch('/api/models')).json();\nconst chatProviders = new Set(['openai', 'anthropic', 'google-antigravity']); // chat-capable providers\nconst target = models.find((m: any) => chatProviders.has((m.provider || '').toLowerCase()));\nif (!target) throw new Error('no default-eligible chat model configured');\nawait postDefault(target.model_name);","typeGuard":"function isDefaultAllowedProvider(provider: string): boolean {\n  const chat = new Set(['openai', 'anthropic', 'google-antigravity']);\n  return chat.has(provider.toLowerCase());\n}","tryCatchPattern":"try {\n  const res = await fetch('/api/models/default', {...});\n  if (res.status === 400 && (await res.text()).includes('default chat model')) {\n    /* restrict picker to chat-capable providers and retry */\n  }\n} catch (e) { /* network */ }","preventionTips":["Only offer chat-capable LLM providers in the default-model selector","Keep utility models (embedding/TTS/ASR) visibly separated from chat models in the UI","When adding a provider, record whether it is default-eligible so the UI can filter"],"tags":["http","validation","provider","default-model"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}