zeroclaw-labs/zeroclaw · error
skills.extra_registries[{i}].name '{}' is invalid; use only
Error message
skills.extra_registries[{i}].name '{}' is invalid; use only lowercase ASCII letters, numbers, '-' or '_' so it can be addressed as registry:<name>/<skill> What it means
ExternalRegistry::is_valid_name gates registry names to lowercase ASCII letters, digits, '-' and '_' only; Config::validate() bails for anything else. The constraint exists because the name is embedded in skill addresses of the form registry:<name>/<skill> and must round-trip through the skill-resolution grammar without quoting; uppercase, spaces, dots, or ':' would make the address ambiguous or unparseable.
Source
Thrown at crates/zeroclaw-config/src/schema.rs:21972
&& !std::path::Path::new(tpl_dir).exists()
{
anyhow::bail!("project_intel.templates_dir path does not exist: {tpl_dir}");
}
}
// Proxy (delegate to existing validation)
self.proxy.validate()?;
self.cloud_ops.validate()?;
// Skills — extra registries
{
let mut seen = std::collections::HashSet::new();
for (i, reg) in self.skills.extra_registries.iter().enumerate() {
if reg.name.trim().is_empty() {
anyhow::bail!("skills.extra_registries[{i}].name must not be empty");
}
if !ExternalRegistry::is_valid_name(®.name) {
anyhow::bail!(
"skills.extra_registries[{i}].name '{}' is invalid; use only lowercase ASCII letters, numbers, '-' or '_' so it can be addressed as registry:<name>/<skill>",
reg.name
);
}
if !seen.insert(reg.name.as_str()) {
anyhow::bail!("skills.extra_registries has duplicate name '{}'", reg.name);
}
if reg.url.trim().is_empty() {
anyhow::bail!(
"skills.extra_registries[{}].url must not be empty",
reg.name
);
}
if reg.kind != ExternalRegistryKind::Git {
anyhow::bail!(
"skills.extra_registries[{}].kind must be 'git' (got '{}'); other protocols are not yet supported",
reg.name,
reg.kindView on GitHub (pinned to 88bb9c8533)
Solutions
- Rewrite the name as a lowercase slug using only a-z, 0-9, '-', '_' (e.g. "team-skills")
- Keep it stable: renaming later changes every registry:<name>/<skill> reference
- Add a separate display/description field elsewhere if a pretty label is needed
Example fix
# before [[skills.extra_registries]] name = "Team Skills" url = "https://git.example.com/team/skills.git" # after [[skills.extra_registries]] name = "team-skills" url = "https://git.example.com/team/skills.git"
Defensive patterns
Strategy: type-guard
Validate before calling
for reg in &cfg.skills.extra_registries {
if !ExternalRegistry::is_valid_name(®.name) { /* reject early */ }
} Type guard
// Mirrors ExternalRegistry::is_valid_name: registry:<name>/<skill> must stay parseable
fn is_valid_registry_name(name: &str) -> bool {
!name.is_empty()
&& name
.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("name '") && e.to_string().contains("is invalid") => {
// slugify the name: lowercase, spaces/dots -> '-'
}
Err(e) => return Err(e),
} Prevention
- Slugify registry names the way you slugify URLs (kebab-case)
- Treat the name as an API surface: changing it breaks registry:<name>/<skill> references
- Keep display labels out of machine keys
When it happens
Trigger: A registry entry with name = "My Registry", name = "Team.Skills", or name = "org/registry" failing ExternalRegistry::is_valid_name during Config::validate().
Common situations: Using a human-friendly display name instead of a slug; copying an org/team name with spaces from a Git host; national characters or smart quotes pasted from documentation.
Related errors
- skills.extra_registries[{}].url is not a valid URL: {e}
- registry '{registry_name}' uses unsupported kind '{}'; only
- skill '{skill_name}' not found in registry '{registry_name}'
- google_workspace.allowed_operations[{i}].service contains in
- google_workspace.allowed_operations[{i}].resource contains i
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/bcff4cde718eb727.
Report an issue: GitHub.