zeroclaw-labs/zeroclaw · error
google_workspace.allowed_operations[{i}].resource contains i
Error message
google_workspace.allowed_operations[{i}].resource contains invalid characters: {resource} What it means
Config::validate() checks that allowed_operations[].resource contains only ASCII alphanumeric characters (both cases), '_' and '-'. Unlike service IDs, camelCase is deliberately accepted here because Google API resource names are camelCase (calendarList, quickUpdate); spaces, dots, slashes and other punctuation are rejected so the resource can be matched unambiguously against runtime tool identifiers.
Source
Thrown at crates/zeroclaw-config/src/schema.rs:21873
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() {
anyhow::bail!(
"google_workspace.allowed_operations[{i}].sub_resource must not be empty when present"
);
}
if !sub
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
{
anyhow::bail!(
"google_workspace.allowed_operations[{i}].sub_resource contains invalid characters: {sub}"
);View on GitHub (pinned to 88bb9c8533)
Solutions
- Use the bare camelCase resource name from the Google API ("calendarList", "files", "messages")
- Replace spaces with '_' or '-' if you need a separator, or drop path prefixes entirely
- Verify the value against the service's API surface so the runtime check can actually match
Example fix
# before [[google_workspace.allowed_operations]] service = "calendar" resource = "calendar/v3/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_resource(s: &str) -> bool {
!s.trim().is_empty()
&& s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
} Type guard
fn is_valid_gws_resource(s: &str) -> bool {
s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
} Try / catch
match config.validate() {
Ok(()) => {}
Err(e) if e.to_string().contains("resource contains invalid characters") => {
// strip path segments/dots; keep the bare camelCase resource name
}
Err(e) => return Err(e),
} Prevention
- Copy resource names from the API's resource listing, not from REST URL paths
- Remember resource allows camelCase but not '.', '/', or spaces
- Add a scratch test that validates your production config file
When it happens
Trigger: An operation entry with resource = "calendar list", resource = "files.attachments", or resource = "a/b" in the config file, surfaced when Config::validate() runs at startup or config check.
Common situations: Transcribing REST paths ("calendar/v3/calendarList") or dot-separated names from API documentation instead of the bare resource name; inserting spaces while hand-editing long TOML arrays.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- google_workspace.allowed_operations[{i}].service contains in
- google_workspace.allowed_operations[{i}].sub_resource contai
- google_workspace.allowed_operations[{i}].methods[{j}] contai
- google_workspace.allowed_operations[{i}].resource must not b
- google_workspace.allowed_operations[{i}].sub_resource must n
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/15ea4dd3456a46ce.
Report an issue: GitHub.