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
- Remove the duplicate entry named in the message
- If config layers merge lists by concatenation, deduplicate before writing the merged file
- 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
- Deduplicate merged arrays before writing layered configs
- Trim entries on input so whitespace variants don't create hidden duplicates
- Add a config-lint step that fails on duplicate list entries across all sections
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
- google_workspace.allowed_services[{i}] contains invalid char
- cloud_ops.iac_tools must not be empty when cloud_ops is enab
- gateway.path_prefix contains invalid character '{bad}'; only
- risk_profiles.{profile_alias}.shell_env_passthrough[{i}] is
- security.otp.cache_valid_secs must be greater than or equal
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/3545e82f1e4c6d36.
Report an issue: GitHub.