{"record":{"id":"c6fca0d1113d96db","repo":"Hmbown/CodeWhale","slug":"custom-models-index-field-is-invalid","errorCode":null,"errorMessage":"custom_models[{index}].{field} is invalid","messagePattern":"custom_models\\[(.+?)\\]\\.(.+?) is invalid","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/catalog/configured.rs","lineNumber":73,"sourceCode":"            endpoint_key: \"chat\".into(),\n            limit: self.limit.clone(),\n            cost: self.cost.clone(),\n            modalities: self.modalities.clone(),\n            attachment: self.attachment,\n            reasoning: self.reasoning,\n            tool_call: self.tool_call,\n            structured_output: self.structured_output,\n            source: CatalogSource::ConfigOverride,\n            ..CatalogOffering::default()\n        }\n    }\n}\n\npub fn validate_configured_models(models: &[ConfiguredModel]) -> anyhow::Result<()> {\n    let mut identities = BTreeSet::new();\n    for (index, model) in models.iter().enumerate() {\n        // Error messages name fields, never echo untrusted values or URLs.\n        let invalid = |field| anyhow::anyhow!(\"custom_models[{index}].{field} is invalid\");\n        for (field, value) in [(\"provider\", &model.provider), (\"id\", &model.id)] {\n            if value.is_empty()\n                || value.len() > 256\n                || value.chars().any(char::is_whitespace)\n                || value.chars().any(char::is_control)\n            {\n                return Err(invalid(field));\n            }\n        }\n        if model.id.eq_ignore_ascii_case(\"auto\") {\n            return Err(invalid(\"id\"));\n        }\n        let Some((scheme, rest)) = model.base_url.split_once(\"://\") else {\n            return Err(invalid(\"base_url\"));\n        };\n        if !matches!(scheme.to_ascii_lowercase().as_str(), \"http\" | \"https\")\n            || rest.split('/').next().is_none_or(str::is_empty)\n            || model.base_url.contains(['@', '?', '#'])","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/catalog/configured.rs#L55-L91","documentation":"`validate_configured_models` enforces structural rules on each entry of the `custom_models` config list: `provider` and `id` must be non-empty, at most 256 chars, and free of whitespace and control characters. The error names the offending entry index and field but deliberately never echoes the invalid value, to avoid leaking untrusted content.","triggerScenarios":"Deserializing config via `deserialize_configured_models` when a `custom_models[i].provider` or `.id` is empty, longer than 256 characters, or contains spaces/tabs/control characters.","commonSituations":"Hand-edited config with a trailing space in the model id, a pasted provider name containing a newline, or a copy-paste that left the field blank.","solutions":["Open the config file and inspect `custom_models[index]` — fix the field named in the message.","Ensure both `provider` and `id` are non-empty, ≤256 chars, with no spaces or control characters.","Trim surrounding whitespace from pasted values and re-run the command."],"exampleFix":"// before (config TOML)\n[[custom_models]]\nprovider = \"openai \"\nid = \"gpt-4o\"\n// after\n[[custom_models]]\nprovider = \"openai\"\nid = \"gpt-4o\"","handlingStrategy":"validation","validationCode":"fn valid_field(s: &str) -> bool {\n    !s.is_empty() && s.len() <= 256\n        && !s.chars().any(char::is_whitespace)\n        && !s.chars().any(char::is_control)\n}\n// run on each custom_models entry before writing config\nassert!(valid_field(&model.provider) && valid_field(&model.id));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Trim pasted provider/id values before saving them to config","Validate custom_models entries at editor time with the same rules (non-empty, ≤256, no whitespace/control chars)","Keep model ids to simple identifier characters (letters, digits, dash, dot)"],"tags":["config","validation","custom-models"],"backgroundTag":"schema-validation-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}