jdx/mise · error

a validated copy_link has a staged parent

Error message

a validated copy_link has a staged parent

What it means

When materializing a `copy_link`, the code takes `staged_link.parent().expect("a validated copy_link has a staged parent")`. Because `staged_link` is built as a joined path, parent() should always exist; None would mean `link` itself is an empty path or the staged root is empty, i.e. the validated link value degenerated.

Solutions

  1. Ensure the copy_link value is a non-empty relative path (e.g. "subdir/target" not "")
  2. Check the staged_source base is a valid non-empty directory before staging
  3. Replace the expect with an error carrying the link value for easier diagnosis

Example fix

// before
copy_link = ""
// after
copy_link = "bin/shared-tool"
Defensive patterns

Strategy: validation

Validate before calling

if link.as_os_str().is_empty() {
    return Err(anyhow!("copy_link path must be non-empty"));
}

Type guard

fn is_valid_link(link: &Path) -> bool {
    !link.as_os_str().is_empty() && link.is_relative()
}

Prevention

When it happens

Trigger: Applying a copy_link (src/system/remote.rs:881) where the `link` relative path is empty or the staged_source base is empty, making `staged_link` a bare root with no parent.

Common situations: A copy_link config entry with an empty or whitespace path; templated link path expanding to empty; base path left unset when joining.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/85e0913ed5d935ee. Report an issue: GitHub.

Appendix: source

Thrown at src/system/remote.rs:881

    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()))?;
    let source_link = source.join(link);
    let staged_link = staged_source.join(link);
    let target = fs::canonicalize(&source_link)
        .wrap_err_with(|| format!("failed to resolve copy_link {}", link.display()))?;
    let parent = staged_link
        .parent()
        .expect("a validated copy_link has a staged parent");
    let original_permissions = make_directory_writable(parent)?;
    let result = (|| {
        crate::file::remove_file(&staged_link)?;
        if target.is_dir() {
            fs::create_dir(&staged_link)?;
            crate::file::copy_dir_all_preserve_symlinks(&target, &staged_link)?;
        } else {
            crate::file::copy(&target, &staged_link)?;
        }
        Ok(())
    })();
    if let Some(permissions) = original_permissions {
        fs::set_permissions(parent, permissions).wrap_err_with(|| {
            format!(
                "failed to restore staged directory permissions: {}",
                parent.display()
            )
        })?;

View on GitHub (pinned to 533346cc37)