Hmbown/CodeWhale · error · anyhow::Error
refusing to copy symlink {}
Error message
refusing to copy symlink {} What it means
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.
Source
Thrown at crates/tui/src/skills/mutation.rs:831
outcome: SkillMutationOutcome::Imported,
})
}
fn copy_skill_package(src: &Path, dest: &Path) -> Result<()> {
// Fail closed: source must already pass package digest (no symlinks).
package_digest::compute_package_digest(src).context("source package is not safe to copy")?;
copy_dir_regular_files(src, dest)?;
Ok(())
}
fn copy_dir_regular_files(src: &Path, dest: &Path) -> Result<()> {
fs::create_dir_all(dest)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let path = entry.path();
let meta = fs::symlink_metadata(&path)?;
if meta.file_type().is_symlink() {
bail!("refusing to copy symlink {}", path.display());
}
let name = entry.file_name();
let name_str = name.to_string_lossy();
if name_str == install::INSTALLED_FROM_MARKER
|| name_str == install::TRUSTED_MARKER
|| name_str == ".system-installed-version"
{
continue;
}
let target = dest.join(&name);
if meta.is_dir() {
if name_str.starts_with('.') {
continue;
}
copy_dir_regular_files(&path, &target)?;
} else if meta.is_file() {
if name_str.starts_with('.') {
continue;View on GitHub (pinned to 0c42157ee5)
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
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at crates/tui/src/skills/mutation.rs:831 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/5069082bcae3ea5b.
Report an issue: GitHub.