{"record":{"id":"25e93c19d062773a","repo":"aaif-goose/goose","slug":"invalid-provider-id-provider-id-cannot-be-empty","errorCode":null,"errorMessage":"Invalid provider id: provider id cannot be empty","messagePattern":"Invalid provider id: provider id cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/goose-providers/src/declarative.rs","lineNumber":395,"sourceCode":"\n        let error = deserialize_provider_config(&definition.to_string()).unwrap_err();\n\n        assert!(error.to_string().contains(\"unknown field `description`\"));\n    }\n\n    fn placeholder_var_names(template: &str) -> Vec<String> {\n        template\n            .split(\"${\")\n            .skip(1)\n            .filter_map(|chunk| chunk.split_once('}'))\n            .map(|(name, _)| name.to_string())\n            .collect()\n    }\n\n    fn validate_provider_id(id: &str) -> Result<()> {\n        let mut chars = id.chars();\n        let Some(first) = chars.next() else {\n            anyhow::bail!(\"Invalid provider id: provider id cannot be empty\");\n        };\n\n        if !(first.is_ascii_lowercase() || first.is_ascii_digit() || first == '_') {\n            anyhow::bail!(\"Invalid provider id: {id}\");\n        }\n\n        if chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_' || ch == '-')\n        {\n            Ok(())\n        } else {\n            anyhow::bail!(\"Invalid provider id: {id}\")\n        }\n    }\n\n    #[test]\n    fn expose_declarative_providers_enumerates_all_bundled_json_files() {\n        let enumerated: HashSet<_> = fixed_provider_config_entries()\n            .into_iter()","sourceCodeStart":377,"sourceCodeEnd":413,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-providers/src/declarative.rs#L377-L413","documentation":"validate_provider_id (declarative.rs, inside the #[cfg(test)] tests module) enforces that a provider's id — which is its 'name' field — is non-empty and matches [a-z0-9_][a-z0-9_-]*. This specific arm fires when id() is the empty string, i.e. the provider JSON has an empty 'name'. It surfaces from tests like all_bundled_providers_are_valid that lint every bundled provider JSON, not from runtime behavior.","triggerScenarios":"Adding or editing a bundled declarative provider JSON (under the FIXED_PROVIDERS/rust-embed directory) with \"name\": \"\" (or a name that trims to empty), then running cargo test — the validation test panics with this message.","commonSituations":"Contributors fork a bundled provider JSON, clear the name to fill in later, and forget; automated JSON generation emits an empty name field; the same lint also guards against duplicate ids and empty base_url, so any schema slip shows up as a test failure.","solutions":["Set a real id: \"name\": \"my-gateway\" — the name doubles as the provider id used in config and dedup","Keep to the id charset: starts with lowercase letter, digit, or underscore; continues with lowercase, digit, underscore, or hyphen","Re-run cargo test -p goose-providers declarative to confirm the bundled-provider lint passes"],"exampleFix":"// before\n{ \"name\": \"\", \"engine\": \"openai_compatible\", ... }\n\n// after\n{ \"name\": \"acme-gateway\", \"engine\": \"openai_compatible\", ... }","handlingStrategy":"validation","validationCode":"fn valid_provider_id(id: &str) -> bool {\n    let mut chars = id.chars();\n    match chars.next() {\n        Some(c) if c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' =>\n            chars.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-'),\n        _ => false, // covers empty string too\n    }\n}","typeGuard":"fn is_valid_provider_id(id: &str) -> bool {\n    !id.is_empty()\n        && id.starts_with(|c: char| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')\n        && id.chars().skip(1).all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-')\n}","tryCatchPattern":"// Runs only in tests; assert early in any test that adds bundled JSON:\nassert!(is_valid_provider_id(config.id()), \"bad id: {}\", config.id());","preventionTips":["Use slug-form ids by convention when authoring provider JSON ([a-z0-9_][a-z0-9_-]*)","Run cargo test -p goose-providers declarative before committing new bundled providers","Generate ids from display names programmatically (lowercase + hyphenate) instead of typing them"],"tags":["provider-setup","json-config","validation","testing"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}