zeroclaw-labs/zeroclaw · error

google_workspace.allowed_services[{i}] contains invalid char

Error message

google_workspace.allowed_services[{i}] contains invalid characters: {normalized}

What it means

Each entry in [google_workspace] allowed_services is normalized, then must consist solely of ASCII lowercase letters, digits, '_', or '-'. Unlike the OTP gated-actions check (which allows uppercase), this allowlist is strictly lowercase, so any uppercase letter, dot, space, or other punctuation fails with the offending value and index. The charset check runs before the duplicate check, so fix characters first.

Source

Thrown at crates/zeroclaw-config/src/schema.rs:21808

            }
        }

        // Google Workspace allowed_services validation
        let mut seen_gws_services = std::collections::HashSet::new();
        for (i, service) in self.google_workspace.allowed_services.iter().enumerate() {
            let normalized = service.trim();
            if normalized.is_empty() {
                validation_bail!(
                    RequiredFieldEmpty,
                    format!("google_workspace.allowed_services[{i}]"),
                    "google_workspace.allowed_services[{i}] must not be empty"
                );
            }
            if !normalized
                .chars()
                .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-')
            {
                anyhow::bail!(
                    "google_workspace.allowed_services[{i}] contains invalid characters: {normalized}"
                );
            }
            if !seen_gws_services.insert(normalized.to_string()) {
                anyhow::bail!(
                    "google_workspace.allowed_services contains duplicate entry: {normalized}"
                );
            }
        }

        // Build the effective allowed-services set for cross-validation.
        // When the operator leaves allowed_services empty the tool falls back to
        // DEFAULT_GWS_SERVICES; use the same constant here so validation is
        // consistent in both cases.
        let effective_services: std::collections::HashSet<&str> =
            if self.google_workspace.allowed_services.is_empty() {
                DEFAULT_GWS_SERVICES.iter().copied().collect()
            } else {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Slugify the entry: lowercase, dots/underscores to '-', or '_' — "Calendar API" -> "calendar", "admin.directory" -> "admin_directory"
  2. Stick to the documented lowercase slug list for allowed services
  3. Re-run validation after each fix — the loop bails on the first invalid entry

Example fix

# before
[google_workspace]
allowed_services = ["Calendar", "drive.google.com"]

# after
[google_workspace]
allowed_services = ["calendar", "drive"]
Defensive patterns

Strategy: validation

Validate before calling

fn valid_gws_service(name: &str) -> bool {
    name.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-')
}

fn gws_services_precheck(services: &[String]) -> Result<(), String> {
    if let Some(bad) = services.iter().find(|s| !valid_gws_service(s)) {
        return Err(format!("invalid allowed_services entry: {bad:?}"));
    }
    Ok(())
}

Type guard

fn is_gws_service_slug(s: &str) -> bool {
    !s.is_empty() && valid_gws_service(s)
}

Try / catch

if let Err(err) = config.validate() {
    if err.to_string().contains("google_workspace.allowed_services") && err.to_string().contains("invalid characters") {
        // lowercase and slugify the entry at the reported index
    }
}

Prevention

When it happens

Trigger: Set allowed_services to entries like "Calendar" (uppercase), "drive.google.com" (dots), "sheets v4" (space), "Admin_Directory" (uppercase), or any non-slug string.

Common situations: Copying API names from Google's API directory where they appear as CamelCase product names or dotted Discovery names (`admin.directory`); mixing product names with service slugs; hand-editing without a reference list.

Understand the failure class

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/08830bc8b6a04c4a. Report an issue: GitHub.