zeroclaw-labs/zeroclaw · error

google_workspace.allowed_services contains duplicate entry:

Error message

google_workspace.allowed_services contains duplicate entry: {normalized}

What it means

A per-section HashSet (seen_gws_services) tracks normalized allowed_services entries; inserting a value that is already in the set bails with the duplicate named. Because normalization (trim, and whatever casing fold precedes the strict-lowercase charset check) happens before insertion, entries differing only in padding whitespace collapse to the same duplicate. The deduped set feeds the effective allowed-services cross-validation that follows, so duplicates are rejected rather than silently merged.

Source

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

        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 {
                self.google_workspace
                    .allowed_services
                    .iter()
                    .map(|s| s.trim())
                    .collect()

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Remove the duplicate entry named in the message
  2. If config layers merge lists by concatenation, deduplicate before writing the merged file
  3. Watch whitespace-only differences — ' gmail' duplicates 'gmail' after normalization

Example fix

# before
[google_workspace]
allowed_services = ["gmail", "drive", "gmail"]

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

Strategy: validation

Validate before calling

fn gws_duplicate_precheck(services: &[String]) -> Result<(), String> {
    let mut seen = std::collections::HashSet::new();
    for s in services {
        let norm = s.trim().to_lowercase();
        if !seen.insert(norm.clone()) {
            return Err(format!("duplicate allowed_services entry: {norm}"));
        }
    }
    Ok(())
}

Type guard

fn gws_services_unique(services: &[String]) -> bool {
    let norm: std::collections::HashSet<String> = services.iter().map(|s| s.trim().to_lowercase()).collect();
    norm.len() == services.len()
}

Try / catch

if let Err(err) = config.validate() {
    if err.to_string().contains("google_workspace.allowed_services contains duplicate") {
        // drop the later occurrence of the named service and reload
    }
}

Prevention

When it happens

Trigger: Set allowed_services = ["gmail", "gmail"], or ["gmail", " gmail"], or merge config layers that each append the same service so the concatenated list contains it twice.

Common situations: Overlay/profile configs that merge arrays by concatenation; hand-edits adding a service that was already listed further down; copy-paste between config revisions.

Related errors


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