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

  1. Set a real id: "name": "my-gateway" — the name doubles as the provider id used in config and dedup
  2. Keep to the id charset: starts with lowercase letter, digit, or underscore; continues with lowercase, digit, underscore, or hyphen
  3. 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

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


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/25e93c19d062773a. Report an issue: GitHub.