{"record":{"id":"3d7f0f2c101ea170","repo":"zeroclaw-labs/zeroclaw","slug":"unable-to-determine-installed-skill-directory-afte-3d7f0f","errorCode":null,"errorMessage":"Unable to determine installed skill directory after clone (multiple new directories found)","messagePattern":"Unable to determine installed skill directory after clone \\(multiple new directories found\\)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-runtime/src/skills/mod.rs","lineNumber":1993,"sourceCode":"fn detect_newly_installed_directory(\n    skills_path: &Path,\n    before: &HashSet<PathBuf>,\n) -> Result<PathBuf> {\n    let mut created = Vec::new();\n    for entry in std::fs::read_dir(skills_path)? {\n        let entry = entry?;\n        let path = entry.path();\n        if !before.contains(&path) && path.is_dir() {\n            created.push(path);\n        }\n    }\n\n    match created.len() {\n        1 => Ok(created.remove(0)),\n        0 => anyhow::bail!(\n            \"Unable to determine installed skill directory after clone (no new directory found)\"\n        ),\n        _ => anyhow::bail!(\n            \"Unable to determine installed skill directory after clone (multiple new directories found)\"\n        ),\n    }\n}\n\nfn enforce_skill_security_audit(\n    skill_path: &Path,\n    allow_scripts: bool,\n) -> Result<audit::SkillAuditReport> {\n    let report = audit::audit_skill_directory_with_options(\n        skill_path,\n        audit::SkillAuditOptions { allow_scripts },\n    )?;\n    if report.is_clean() {\n        return Ok(report);\n    }\n\n    anyhow::bail!(\"Skill security audit failed: {}\", report.summary());","sourceCodeStart":1975,"sourceCodeEnd":2011,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-runtime/src/skills/mod.rs#L1975-L2011","documentation":"Same before/after directory diff in detect_newly_installed_directory, but two or more new directories appeared. git clone creates exactly one directory, so the extras came from something else writing into the skills directory between the snapshot and the scan.","triggerScenarios":"Two concurrent skill installs into the same workspace; an agent loop, file watcher, or skill-improvement job creating directories under skills/ at the moment of the install.","commonSituations":"Parallel automation invoking skills install; a background process materializing skill directories while an install is in flight.","solutions":["Serialize installs with a workspace-level lock or run them one at a time","Inspect skills/ for the unexpected new directories, remove them if unwanted, and retry","Identify what else writes into the workspace skills dir and pause it during installs"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Serialize in-process installs:\nstatic INSTALL_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());\nlet _guard = INSTALL_LOCK.lock().unwrap_or_else(|p| p.into_inner());\ninstall_git_skill_source(url, &skills_path, allow_scripts)?;","typeGuard":null,"tryCatchPattern":"match install_git_skill_source(...) {\n    Err(e) if e.to_string().contains(\"multiple new directories found\") => {\n        // list skills/ entries created after start, remove intruders, retry\n    }\n    r => r,\n}","preventionTips":["Never run concurrent skills installs against one workspace","Use a workspace-level lock across processes (flock on a lock file) if multiple tools install skills","Keep background jobs that create skill directories paused during installs"],"tags":["skills","install","git-clone","concurrency"],"backgroundTag":"clone-artifact-detection-failed","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}