aaif-goose/goose · warning
Invalid provider id: provider id cannot be empty
Error message
Invalid provider id: provider id cannot be empty
What it means
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.
Source
Thrown at crates/goose-providers/src/declarative.rs:395
let error = deserialize_provider_config(&definition.to_string()).unwrap_err();
assert!(error.to_string().contains("unknown field `description`"));
}
fn placeholder_var_names(template: &str) -> Vec<String> {
template
.split("${")
.skip(1)
.filter_map(|chunk| chunk.split_once('}'))
.map(|(name, _)| name.to_string())
.collect()
}
fn validate_provider_id(id: &str) -> Result<()> {
let mut chars = id.chars();
let Some(first) = chars.next() else {
anyhow::bail!("Invalid provider id: provider id cannot be empty");
};
if !(first.is_ascii_lowercase() || first.is_ascii_digit() || first == '_') {
anyhow::bail!("Invalid provider id: {id}");
}
if chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_' || ch == '-')
{
Ok(())
} else {
anyhow::bail!("Invalid provider id: {id}")
}
}
#[test]
fn expose_declarative_providers_enumerates_all_bundled_json_files() {
let enumerated: HashSet<_> = fixed_provider_config_entries()
.into_iter()View on GitHub (pinned to 3810898a74)
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
Example fix
// before
{ "name": "", "engine": "openai_compatible", ... }
// after
{ "name": "acme-gateway", "engine": "openai_compatible", ... } Defensive patterns
Strategy: validation
Validate before calling
fn valid_provider_id(id: &str) -> bool {
let mut chars = id.chars();
match chars.next() {
Some(c) if c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' =>
chars.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-'),
_ => false, // covers empty string too
}
} Type guard
fn is_valid_provider_id(id: &str) -> bool {
!id.is_empty()
&& id.starts_with(|c: char| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
&& id.chars().skip(1).all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-')
} Try / catch
// Runs only in tests; assert early in any test that adds bundled JSON:
assert!(is_valid_provider_id(config.id()), "bad id: {}", config.id()); Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid provider id: {id}
- Invalid provider type: {}
- Failed to parse {}: {}
- Provider '{}' has dynamic_models: false but no static models
- No messages found in scenario result
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/25e93c19d062773a.
Report an issue: GitHub.