zeroclaw-labs/zeroclaw · error · anyhow::Error
skill '{$skill}' not found in {$url}: no skills/ directory,
Error message
skill '{$skill}' not found in {$url}: no skills/ directory, or it is empty What it means
After cloning the catalog repo into a temp dir, symlink_metadata on <clone>/skills returned NotFound: the catalog has no skills/ directory at its root. The catalog layout contract is <repo>/skills/<skill-name>/, so a repo without a root skills/ directory cannot serve as a skills catalog.
Source
Thrown at crates/zeroclaw-runtime/src/skills/mod.rs:2398
)
})?;
(|| {
// Establish the catalog trust boundary before looking up a selected
// name or enumerating available names. A catalog controls `skills/`,
// so following it before this check could inspect an arbitrary host
// directory even when the requested skill does not exist.
let clone_root = clone_dir.canonicalize().with_context(|| {
format!(
"failed to canonicalize catalog clone {}",
clone_dir.display()
)
})?;
let skills_dir = clone_dir.join("skills");
let skills_meta = match std::fs::symlink_metadata(&skills_dir) {
Ok(metadata) => metadata,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
anyhow::bail!(crate::i18n::get_required_cli_string_with_args(
"cli-skills-install-skill-not-in-catalog-empty",
&[("skill", skill_name), ("url", url)]
));
}
Err(err) => {
return Err(err).with_context(|| {
format!(
"failed to read metadata for catalog skills root {}",
skills_dir.display()
)
});
}
};
if skills_meta.file_type().is_symlink() {
anyhow::bail!(crate::i18n::get_required_cli_string_with_args(
"cli-skills-install-catalog-root-symlink",
&[("url", url)]
));View on GitHub (pinned to 88bb9c8533)
Solutions
- Check the repo: it must contain a skills/ directory at the root with one directory per skill
- If the skills live elsewhere, clone the repo yourself and install the wanted folder via the local-source installer
- Use a catalog repo that follows the <repo>/skills/<name>/ layout
Defensive patterns
Strategy: try-catch
Validate before calling
// Optional pre-flight: confirm the repo looks like a catalog before offering it
let out = std::process::Command::new("git")
.args(["ls-remote", url])
.output()?;
if !out.status.success() {
anyhow::bail!("catalog repo unreachable: {url}");
}
// layout itself can only be verified after clone — handle at runtime Try / catch
match install_git_catalog_skill_source(url, skill, &skills_path, false, &ws) {
Err(e) if e.to_string().contains("no skills/ directory, or it is empty")
&& !e.to_string().contains("Available skills") => {
// catalog lacks the <repo>/skills/ layout: use a proper catalog repo,
// or clone manually and install the folder via the local installer
}
r => r,
} Prevention
- Only advertise catalog URLs you have verified contain a root skills/ directory
- Document the <repo>/skills/<name>/ layout contract for anyone building catalogs
- Fall back to local-source install when a repo has skills in a nonstandard place
When it happens
Trigger: URL points at a repo that stores skills at the repository root or under a different folder (skill/, packages/); the URL points at an ordinary project repo rather than a skills catalog.
Common situations: Pointing the catalog install at a normal project repository; a catalog repo that changed its layout between versions.
Related errors
- skill '{$skill}' not found in {$url}. Available skills: {$av
- skill '{source}' not found in the registry and no skills are
- skill '{source}' not found in the registry. Available skills
- skill '{skill_name}' not found in registry '{registry_name}'
- skill '{skill_name}' not found in registry '{registry_name}'
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/2a556133ef110693.
Report an issue: GitHub.