zeroclaw-labs/zeroclaw · error · anyhow::Error
skill catalog {$url} has a symlinked skills/ directory; refu
Error message
skill catalog {$url} has a symlinked skills/ directory; refusing to inspect it What it means
The catalog's skills/ entry is a symlink, and the installer refuses to inspect it: a catalog-controlled symlink could redirect skill lookup to an arbitrary directory on the host. This trust-boundary check runs before any skill name is resolved or available names are enumerated.
Source
Thrown at crates/zeroclaw-runtime/src/skills/mod.rs:2413
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)]
));
}
let skills_root = skills_dir.canonicalize().with_context(|| {
format!(
"failed to canonicalize catalog skills root {}",
skills_dir.display()
)
})?;
if !skills_root.starts_with(&clone_root) {
anyhow::bail!(crate::i18n::get_required_cli_string_with_args(
"cli-skills-install-catalog-root-escapes",
&[("url", url)]
));
}
if !skills_root.is_dir() {
anyhow::bail!(crate::i18n::get_required_cli_string_with_args(View on GitHub (pinned to 88bb9c8533)
Solutions
- Do not use that repository as a catalog; if unexpected, treat it as suspicious
- Fork the catalog and replace the symlinked skills entry with a real directory, then install from the fork
Defensive patterns
Strategy: try-catch
Try / catch
match install_git_catalog_skill_source(url, skill, &skills_path, false, &ws) {
Err(e) if e.to_string().contains("symlinked skills/ directory") => {
// catalog is untrusted/broken: stop using this URL; do not attempt to
// bypass — the check protects the host filesystem
}
r => r,
} Prevention
- Treat a symlinked skills/ catalog as hostile; report it upstream
- Verify catalogs from third parties before distributing them to users
- Never 'fix' this by pre-cloning and pointing the installer at the resolved target
When it happens
Trigger: A catalog repo that commits 'skills' as a symlink (Git supports committing symlinks) — either a deliberately tricky/malicious catalog or an exotic hand-built layout.
Common situations: Security testing catalogs; repos prepared on systems where symlinks were accidentally committed; adversarial-catalog research.
Related errors
- skill '{$skill}' in {$url} is a symlink; catalog skills must
- Refusing to copy symlinked skill source path: {}
- Refusing to copy symlink within skill source: {}
- skill catalog {$url} has a skills/ directory that resolves o
- skill '{$skill}' in {$url} resolves outside the cloned catal
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/2bc7c0fb14bd21e7.
Report an issue: GitHub.