{"record":{"id":"e2634c8a5dd9ff80","repo":"Hmbown/CodeWhale","slug":"custom-provider-provider-id-must-set-provider","errorCode":null,"errorMessage":"custom provider '{provider_id}' must set [providers.{provider_id}].kind = \"openai-compatible\"","messagePattern":"custom provider '(.+?)' must set \\[providers\\.(.+?)\\]\\.kind = \"openai-compatible\"","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/config/src/lib.rs","lineNumber":2629,"sourceCode":"            .extras\n            .get(provider_id)\n            .and_then(toml::Value::as_table)\n            .with_context(|| {\n                format!(\n                    \"custom provider '{provider_id}' requires a matching [providers.{provider_id}] table\"\n                )\n            })?;\n        let compatible = table\n            .get(\"kind\")\n            .and_then(toml::Value::as_str)\n            .is_some_and(|kind| {\n                kind.trim()\n                    .to_ascii_lowercase()\n                    .replace('_', \"-\")\n                    .eq(\"openai-compatible\")\n            });\n        if !compatible {\n            bail!(\n                \"custom provider '{provider_id}' must set [providers.{provider_id}].kind = \\\"openai-compatible\\\"\"\n            );\n        }\n        Ok(table)\n    }\n\n    fn named_custom_provider_config(&self) -> Option<ProviderConfigToml> {\n        let provider_id = self.named_custom_provider_id()?;\n        self.named_custom_provider_table(provider_id).ok()?;\n        self.providers\n            .extras\n            .get(provider_id)\n            .cloned()?\n            .try_into()\n            .ok()\n    }\n\n    /// Mutable access to a custom provider's `[providers.<id>]` table,","sourceCodeStart":2611,"sourceCodeEnd":2647,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/config/src/lib.rs#L2611-L2647","documentation":"named_custom_provider_table (crates/config/src/lib.rs:2629) requires every named custom provider table to declare kind = \"openai-compatible\". The check is lenient about case and underscores (trimmed, lowercased, '_' -> '-' before comparing), but the key must be present and match. Custom providers ride the OpenAI-compatible wire, so an untyped table cannot be loaded.","triggerScenarios":"Adding [providers.myprov] to config.toml without a kind key, setting kind to another value (e.g. \"anthropic\", \"openai\"), or misspelling the value so the normalized comparison fails.","commonSituations":"Hand-editing config.toml to add a self-hosted/local model gateway (llama.cpp, vllm, Ollama OpenAI endpoint), copying a built-in provider block as a template and forgetting the kind field.","solutions":["Add kind = \"openai-compatible\" under [providers.<id>] and set base_url/model","If you meant a built-in provider, use its canonical id instead of a custom table","Use `config set providers.<id>.kind openai-compatible` instead of hand-editing so the value is normalized for you"],"exampleFix":"# before\n[providers.local]\nbase_url = \"http://localhost:8080/v1\"\nmodel = \"llama3\"\n\n# after\n[providers.local]\nkind = \"openai-compatible\"\nbase_url = \"http://localhost:8080/v1\"\nmodel = \"llama3\"","handlingStrategy":"validation","validationCode":"fn is_openai_compatible_kind(kind: &str) -> bool {\n    kind.trim().to_ascii_lowercase().replace('_', \"-\") == \"openai-compatible\"\n}\nassert!(is_openai_compatible_kind(kind)); // before writing the provider table","typeGuard":"fn valid_custom_provider_table(t: &toml::Value) -> bool {\n    t.get(\"kind\").and_then(toml::Value::as_str).is_some_and(|k| k.trim().to_ascii_lowercase().replace('_', \"-\") == \"openai-compatible\")\n}","tryCatchPattern":"match load_named_custom_provider() {\n    Ok(cfg) => { /* use */ }\n    Err(e) if e.to_string().contains(\"kind = \\\"openai-compatible\\\"\") => { /* add the kind key, retry */ }\n    Err(e) => return Err(e),\n}","preventionTips":["Always create custom providers via `config set providers.<id>.kind openai-compatible` first","Include the kind line in any copy-paste provider template","Validate config.toml loads after hand edits"],"tags":["rust","config","toml","provider","validation"],"backgroundTag":"missing-required-config-key","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}