{"record":{"id":"80f77d1bd8947b0c","repo":"Hmbown/CodeWhale","slug":"invalid-default-text-model-model-for-provider","errorCode":null,"errorMessage":"Invalid default_text_model '{model}' for provider '{}': expected auto or a model ID this provider serves{hint}.","messagePattern":"Invalid default_text_model '(.+?)' for provider '(.+?)': expected auto or a model ID this provider serves(.+?)\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/config.rs","lineNumber":4698,"sourceCode":"        // (GLM via Z.ai, Kimi, MiniMax, …) and passes unknown ids through, so\n        // it rejects only what the provider genuinely cannot serve. Validating\n        // with the DeepSeek-only `normalize_model_name` bricked every config\n        // whose provider owns a non-DeepSeek family — including ones our own\n        // setup wizard writes (`provider = \"zai\"`, `GLM-5.2`). (#4829)\n        if let Some(model) = self.default_text_model.as_deref()\n            && !model.trim().eq_ignore_ascii_case(\"auto\")\n            && !provider_passes_model_through(self.api_provider())\n            && !self.active_provider_preserves_custom_base_url_model()\n            && canonical_model_id_for_provider(self.api_provider(), model).is_none()\n        {\n            let provider = self.api_provider();\n            let known = model_completion_names_for_provider(provider);\n            let hint = if known.is_empty() {\n                String::new()\n            } else {\n                format!(\" (for example: {})\", known.join(\", \"))\n            };\n            anyhow::bail!(\n                \"Invalid default_text_model '{model}' for provider '{}': expected auto or a model ID this provider serves{hint}.\",\n                provider.as_str()\n            );\n        }\n        if let Some(policy) = self.approval_policy.as_deref() {\n            let normalized = policy.trim().to_ascii_lowercase();\n            if !matches!(\n                normalized.as_str(),\n                \"on-request\" | \"untrusted\" | \"never\" | \"auto\" | \"suggest\"\n            ) {\n                anyhow::bail!(\n                    \"Invalid approval_policy '{policy}': expected on-request, untrusted, never, auto, or suggest.\"\n                );\n            }\n        }\n        if let Some(v) = self.verbosity.as_deref() {\n            let normalized = v.trim().to_ascii_lowercase();\n            if !matches!(normalized.as_str(), \"normal\" | \"concise\") {","sourceCodeStart":4680,"sourceCodeEnd":4716,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/config.rs#L4680-L4716","documentation":"`default_text_model` must be `auto` or a model ID the active provider actually serves, resolved by `canonical_model_id_for_provider` (each family's own canonical map; unknown IDs pass through only for pass-through providers or custom base URLs). Since #4829 validation uses the active provider's namespace, not DeepSeek's — and the error lists that provider's known model names as examples.","triggerScenarios":"`provider = \"deepseek\"` with `default_text_model = \"gpt-4o\"`; a typo'd GLM/Kimi/MiniMax model id; mixing a model ID from one family with a different provider.","commonSituations":"Switching providers without updating the model line; stale IDs after a provider renamed its models; copy-pasting model lists from another provider's docs.","solutions":["Set `default_text_model = \"auto\"` and let the provider choose.","Use one of the example IDs listed in the error's `(for example: ...)` hint.","If the ID is correct for your own endpoint, route it through that custom provider/base URL so it passes through unvalidated.","Check casing and version suffixes of the model ID."],"exampleFix":"# before\nprovider = \"deepseek\"\ndefault_text_model = \"gpt-4o\"\n\n# after\nprovider = \"deepseek\"\ndefault_text_model = \"deepseek-chat\"","handlingStrategy":"validation","validationCode":"fn model_is_acceptable(provider: ApiProvider, model: &str) -> bool {\n    model.trim().eq_ignore_ascii_case(\"auto\")\n        || provider_passes_model_through(provider)\n        || canonical_model_id_for_provider(provider, model).is_some()\n}\n\nassert!(model_is_acceptable(active_provider, &config.default_text_model));","typeGuard":"fn is_valid_model_for_provider(provider: ApiProvider, model: &str) -> bool {\n    model.trim().eq_ignore_ascii_case(\"auto\")\n        || canonical_model_id_for_provider(provider, model).is_some()\n}","tryCatchPattern":null,"preventionTips":["When switching providers, switch the model line in the same edit.","Use `auto` unless you specifically need a pinned model.","Use the provider's own model listing as the source for IDs; do not cross families."],"tags":["config","model-id","provider","validation"],"backgroundTag":"invalid-model-id","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}