zeroclaw-labs/zeroclaw · error

google_workspace.allowed_operations[{i}].service contains in

Error message

google_workspace.allowed_operations[{i}].service contains invalid characters: {service}

What it means

Config::validate() enforces an ID-style character set for allowed_operations[].service: only ASCII lowercase letters, ASCII digits, '_' and '-' are accepted. Google service IDs are lowercase identifiers (calendar, gmail, drive, chat), so any uppercase letter, space, dot, or non-ASCII character is rejected at load time before the runtime allowlist is built.

Source

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

                );
            }
            if resource.is_empty() {
                anyhow::bail!(
                    "google_workspace.allowed_operations[{i}].resource must not be empty"
                );
            }

            if !effective_services.contains(service) {
                anyhow::bail!(
                    "google_workspace.allowed_operations[{i}].service '{service}' is not in the \
                     effective allowed_services; this entry can never match at runtime"
                );
            }
            if !service
                .chars()
                .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-')
            {
                anyhow::bail!(
                    "google_workspace.allowed_operations[{i}].service contains invalid characters: {service}"
                );
            }
            // Unlike service IDs, resource/sub_resource/method names are camelCase
            // in the Google APIs (calendarList, quickAdd, batchUpdate), so
            // uppercase must be accepted here and in the runtime tool check.
            if !resource
                .chars()
                .all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
            {
                anyhow::bail!(
                    "google_workspace.allowed_operations[{i}].resource contains invalid characters: {resource}"
                );
            }

            if let Some(ref sub_resource) = operation.sub_resource {
                let sub = sub_resource.trim();
                if sub.is_empty() {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Rewrite the service ID in lowercase snake/kebab form ("calendar", "gmail", "drive")
  2. Remove surrounding whitespace and dots/dashes used as decoration
  3. Cross-check the corrected value against the allowed_services list to also satisfy the membership check

Example fix

# before
[[google_workspace.allowed_operations]]
service = "Calendar"
resource = "calendarList"
methods = ["list"]

# after
[[google_workspace.allowed_operations]]
service = "calendar"
resource = "calendarList"
methods = ["list"]
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_gws_service_id(s: &str) -> bool {
    !s.trim().is_empty()
        && s.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-')
}

Type guard

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

Try / catch

match config.validate() {
    Ok(()) => {}
    Err(e) if e.to_string().contains("service contains invalid characters") => {
        // lowercase the service id in the TOML source and re-validate
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: An operation entry with service = "Calendar", service = "google calendar", service = "gcal.0", or any value containing uppercase/whitespace/punctuation, hit when the config is deserialized and Config::validate() runs.

Common situations: Copy-pasting display names or product names from Google's console ("Google Calendar"), using the camelCase habit that IS valid for resource/method fields but not for service IDs, or trailing spaces from manual editing.

Understand the failure class

Related errors


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