{"record":{"id":"5069082bcae3ea5b","repo":"Hmbown/CodeWhale","slug":"refusing-to-copy-symlink","errorCode":null,"errorMessage":"refusing to copy symlink {}","messagePattern":"refusing to copy symlink (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/skills/mutation.rs","lineNumber":831,"sourceCode":"        outcome: SkillMutationOutcome::Imported,\n    })\n}\n\nfn copy_skill_package(src: &Path, dest: &Path) -> Result<()> {\n    // Fail closed: source must already pass package digest (no symlinks).\n    package_digest::compute_package_digest(src).context(\"source package is not safe to copy\")?;\n    copy_dir_regular_files(src, dest)?;\n    Ok(())\n}\n\nfn copy_dir_regular_files(src: &Path, dest: &Path) -> Result<()> {\n    fs::create_dir_all(dest)?;\n    for entry in fs::read_dir(src)? {\n        let entry = entry?;\n        let path = entry.path();\n        let meta = fs::symlink_metadata(&path)?;\n        if meta.file_type().is_symlink() {\n            bail!(\"refusing to copy symlink {}\", path.display());\n        }\n        let name = entry.file_name();\n        let name_str = name.to_string_lossy();\n        if name_str == install::INSTALLED_FROM_MARKER\n            || name_str == install::TRUSTED_MARKER\n            || name_str == \".system-installed-version\"\n        {\n            continue;\n        }\n        let target = dest.join(&name);\n        if meta.is_dir() {\n            if name_str.starts_with('.') {\n                continue;\n            }\n            copy_dir_regular_files(&path, &target)?;\n        } else if meta.is_file() {\n            if name_str.starts_with('.') {\n                continue;","sourceCodeStart":813,"sourceCodeEnd":849,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/skills/mutation.rs#L813-L849","documentation":"Raised in copy_dir_regular_files during a skill package copy when a directory entry is a symlink. The copier only walks regular files and directories; a symlink in the source would either duplicate content outside the package or break the digest guarantees, so copying fails closed.","triggerScenarios":"Thrown at crates/tui/src/skills/mutation.rs:831 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Replace symlinks in the skill package with regular files or directories","Re-audit the source package; compute_package_digest should have rejected it, so the package was modified after audit","Re-install the external skill package from a clean source"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}