aaif-goose/goose · warning · anyhow::Error

Invalid provider id: {id}

Error message

Invalid provider id: {id}

What it means

validate_provider_id (in declarative.rs's #[cfg(test)] module) rejects a provider id whose first character is not an ASCII lowercase letter, digit, or underscore. The id is the provider JSON's 'name' field, and this first-character rule is stricter than the rest of the string (a leading hyphen is not allowed even though hyphens are fine later). It fires from bundled-provider validation tests, not at runtime.

Source

Thrown at crates/goose-providers/src/declarative.rs:399

    }

    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()
            .map(|(path, _)| path.to_string())
            .collect();
        let bundled: HashSet<_> = FIXED_PROVIDERS
            .files()

View on GitHub (pinned to 3810898a74)

Solutions

  1. Rename so the id starts with a lowercase letter, digit, or underscore: 'my-provider' instead of 'My-provider' or '-provider'
  2. If the display name needs capitals/style, keep 'name' slug-form and put the pretty text in 'display_name'
  3. Re-run the declarative provider tests after fixing

Example fix

// before
{ "name": "AcmeGateway", "display_name": "Acme", ... }

// after
{ "name": "acme_gateway", "display_name": "Acme Gateway", ... }
Defensive patterns

Strategy: validation

Validate before calling

fn id_first_char_ok(id: &str) -> bool {
    id.chars().next().is_some_and(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
}

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

// In generator scripts, fix ids automatically instead of failing:
let id = id.to_ascii_lowercase().trim_start_matches(['-', '.']).to_string();
assert!(is_valid_provider_id(&id));

Prevention

When it happens

Trigger: A bundled provider JSON with "name": "-gateway", "name": ".local", "name": "MyProvider" (uppercase first letter), or any leading symbol/punctuation — caught when cargo test runs all_bundled_providers_are_valid.

Common situations: Team naming conventions that start ids with uppercase brand names or hyphenated prefixes; porting provider ids from other ecosystems (npm scopes like '@acme/x' or '.local' style) that don't match goose's slug rules.

Related errors


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