jdx/mise · error

copy_link cannot be nested below a symbolic link: {}

Error message

copy_link cannot be nested below a symbolic link: {}

What it means

While validating a copy_link, mise walks each parent component of the link using symlink_metadata and rejects the link if any intermediate component is itself a symlink. Resolving through a symlinked directory would make the final location ambiguous and could escape the staged source, so it fails early.

Source

Thrown at src/system/remote.rs:854

                component,
                Component::ParentDir | Component::RootDir | Component::Prefix(_)
            )
        })
    {
        bail!(
            "copy_link must be a relative path within the source: {}",
            link.display()
        );
    }
    let mut parent = source.to_path_buf();
    for component in link.parent().unwrap_or_else(|| Path::new("")).components() {
        if let Component::Normal(component) = component {
            parent.push(component);
            let metadata = fs::symlink_metadata(&parent).wrap_err_with(|| {
                format!("copy_link parent does not exist: {}", parent.display())
            })?;
            if metadata.file_type().is_symlink() {
                bail!(
                    "copy_link cannot be nested below a symbolic link: {}",
                    link.display()
                );
            }
        }
    }
    let path = source.join(link);
    let metadata = fs::symlink_metadata(&path)
        .wrap_err_with(|| format!("copy_link does not exist: {}", link.display()))?;
    if !metadata.file_type().is_symlink() {
        bail!("copy_link is not a symbolic link: {}", link.display());
    }
    Ok(())
}

fn materialize_link(source: &Path, staged_source: &Path, link: &Path) -> Result<()> {
    validate_copy_link_path(staged_source, link)
        .wrap_err_with(|| format!("staged copy_link is unsafe: {}", link.display()))?;

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Point copy_link at a path whose parent directories are real directories
  2. Replace the intermediate symlink with a real directory (or copy its contents)
  3. Adjust the link to target the symlink's final destination directly

Example fix

// before
copy_link = "bin/tool" // bin is a symlink
// after
copy_link = "scripts/bin/tool"
Defensive patterns

Strategy: validation

Validate before calling

function parentIsRealDir(root, rel) {
  let cur = root;
  for (const seg of path.posix.dirname(rel).split('/').filter(Boolean)) {
    cur = path.join(cur, seg);
    const st = fs.lstatSync(cur);
    if (st.isSymbolicLink()) return false;
  }
  return true;
}

Prevention

When it happens

Trigger: The configured copy_link has a parent directory that is a symbolic link inside the staged source (e.g. link = "bin/tool" where bin is a symlink to ../scripts).

Common situations: Projects that restructure directories with symlinks (bin -> scripts/bin) then add copy_link entries; also generated layouts where CI creates symlinked folders before staging.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/c44fd92b0f210158. Report an issue: GitHub.