{"record":{"id":"838a72a179d6a661","repo":"zeroclaw-labs/zeroclaw","slug":"destination-skill-already-exists","errorCode":null,"errorMessage":"Destination skill already exists: {}","messagePattern":"Destination skill already exists: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/skills/mod.rs","lineNumber":2099,"sourceCode":"    skills_path: &Path,\n    allow_scripts: bool,\n) -> Result<(PathBuf, usize)> {\n    let source_path = PathBuf::from(source);\n    if !source_path.exists() {\n        anyhow::bail!(\"Source path does not exist: {source}\");\n    }\n\n    let source_path = source_path\n        .canonicalize()\n        .with_context(|| format!(\"failed to canonicalize source path {source}\"))?;\n    let _ = enforce_skill_security_audit(&source_path, allow_scripts)?;\n\n    let name = source_path\n        .file_name()\n        .context(\"Source path must include a directory name\")?;\n    let dest = skills_path.join(name);\n    if dest.exists() {\n        anyhow::bail!(\n            \"Destination skill already exists: {}\",\n            dest.display().to_string()\n        );\n    }\n\n    if let Err(err) = copy_dir_recursive_secure(&source_path, &dest) {\n        let _ = std::fs::remove_dir_all(&dest);\n        return Err(err);\n    }\n\n    match enforce_skill_security_audit(&dest, allow_scripts) {\n        Ok(report) => Ok((dest, report.files_scanned)),\n        Err(err) => {\n            let _ = std::fs::remove_dir_all(&dest);\n            Err(err)\n        }\n    }\n}","sourceCodeStart":2081,"sourceCodeEnd":2117,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/skills/mod.rs#L2081-L2117","documentation":"install_local_skill_source derives the install name from the source directory's file_name and refuses to proceed when skills/<name> already exists — it never overwrites an installed skill. Any local source whose folder name matches an installed skill collides, regardless of where it came from.","triggerScenarios":"Reinstalling a skill to upgrade it; installing a different skill whose directory has the same name as one already present; leftover directory from a previous manual copy.","commonSituations":"Upgrading by installing a fresh checkout; two skills from different authors both named e.g. code-review.","solutions":["Remove the installed copy first (uninstall the skill or delete skills/<name>), then install again","If you need both versions, rename the source directory so the installed names differ","Check for leftover partial directories from earlier failed installs"],"exampleFix":"# before\nzeroclaw skills install ~/src/my-skill   # Destination skill already exists: .../skills/my-skill\n\n# after\nzeroclaw skills uninstall my-skill && zeroclaw skills install ~/src/my-skill","handlingStrategy":"validation","validationCode":"let name = std::path::Path::new(source)\n    .file_name().and_then(|n| n.to_str())\n    .context(\"source must end in a directory name\")?;\nif skills_path.join(name).exists() {\n    anyhow::bail!(\"skill '{name}' already installed; uninstall first\");\n}\ninstall_local_skill_source(source, &skills_path, allow_scripts)?;","typeGuard":"fn install_would_collide(source: &str, skills_path: &std::path::Path) -> Option<std::path::PathBuf> {\n    let name = std::path::Path::new(source).file_name()?;\n    let dest = skills_path.join(name);\n    dest.exists().then_some(dest)\n}","tryCatchPattern":null,"preventionTips":["Implement uninstall-then-install as your upgrade path","Derive the destination name the same way the installer does: the source directory's file_name","Rename source folders when you need two variants installed side by side"],"tags":["skills","install","conflict","filesystem"],"backgroundTag":"destination-already-exists","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}