zeroclaw-labs/zeroclaw · error · anyhow::Error

skill '{$skill}' not found in {$url}. Available skills: {$av

Error message

skill '{$skill}' not found in {$url}.
Available skills: {$available}

What it means

The requested skill name has no matching directory in the catalog's skills/ root, and the catalog does contain other skills — the message lists them comma-separated (from list_contained_catalog_skill_names, sorted) so the correct names can be picked immediately. Names are matched as literal directory names: case and separators matter.

Source

Thrown at crates/zeroclaw-runtime/src/skills/mod.rs:2448

        if !skills_root.is_dir() {
            anyhow::bail!(crate::i18n::get_required_cli_string_with_args(
                "cli-skills-install-skill-not-in-catalog-empty",
                &[("skill", skill_name), ("url", url)]
            ));
        }

        let skill_dir = skills_root.join(skill_name);
        let entry_meta = match std::fs::symlink_metadata(&skill_dir) {
            Ok(metadata) => metadata,
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
                let available = list_contained_catalog_skill_names(&skills_root);
                if available.is_empty() {
                    anyhow::bail!(crate::i18n::get_required_cli_string_with_args(
                        "cli-skills-install-skill-not-in-catalog-empty",
                        &[("skill", skill_name), ("url", url)]
                    ));
                }
                anyhow::bail!(crate::i18n::get_required_cli_string_with_args(
                    "cli-skills-install-skill-not-in-catalog",
                    &[
                        ("skill", skill_name),
                        ("url", url),
                        ("available", &available.join(", ")),
                    ]
                ));
            }
            Err(err) => {
                return Err(err).with_context(|| {
                    format!(
                        "failed to read metadata for selected catalog skill {}",
                        skill_dir.display()
                    )
                });
            }
        };
        if entry_meta.file_type().is_symlink() {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Pick the exact name from the 'Available skills' list embedded in the error message
  2. Mind case and separators — the name must equal the directory name under skills/ exactly
  3. If you maintain the catalog, add a skills/<name>/ directory for the missing skill

Example fix

# before
zeroclaw skills install --url <catalog-url> --skill web-scraper
# error: ... Available skills: web_scraper, web-scraper-v2

# after
zeroclaw skills install --url <catalog-url> --skill web-scraper-v2
Defensive patterns

Strategy: try-catch

Try / catch

match install_git_catalog_skill_source(url, skill, &skills_path, false, &ws) {
    Err(e) => {
        let msg = e.to_string();
        if let Some(idx) = msg.find("Available skills: ") {
            let names: Vec<&str> = msg[idx + "Available skills: ".len()..].split(", ").collect();
            // offer the user a pick from `names`; retry with the exact choice
        }
        Err(e)
    }
    r => r,
}

Prevention

When it happens

Trigger: Typo or case mismatch in the skill name; the skill was renamed upstream; requesting 'foo' when the catalog ships 'foo-v2'; hyphen vs underscore confusion (my_skill vs my-skill).

Common situations: Version-suffixed names after a catalog refactor; stale docs or tutorials referencing old names; keyboard autocorrect altering names.

Related errors


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