jdx/mise · error

refusing to overwrite non-empty directory

Error message

refusing to overwrite non-empty directory {}; pass {} or choose an empty/new path

What it means

This error is thrown by the install-into command when the target install directory exists, is non-empty, and the user has not confirmed proceeding (no --yes and declined the interactive prompt). It protects against clobbering an existing tool installation directory with a new install.

Solutions

  1. Re-run with --yes to confirm overwriting the non-empty directory
  2. Remove or move the existing directory contents first, or choose an empty/new install path
  3. In CI, pre-clean the target directory (rm -rf) before installing

Example fix

// before
mise install-into node 22.0.0 ~/.local/share/mise/installs/node/22.0.0
// after
mise install-into node 22.0.0 ~/.local/share/mise/installs/node/22.0.0 --yes
Defensive patterns

Strategy: validation

Validate before calling

INSTALL_PATH="$HOME/.local/share/mise/installs/node/22.0.0"
if [[ -d "$INSTALL_PATH" ]] && [[ -n "$(ls -A "$INSTALL_PATH" 2>/dev/null)" ]]; then
  rm -rf "$INSTALL_PATH"   # or pass --yes to mise install-into
fi

Prevention

When it happens

Trigger: Running `mise install-into` into a directory that already contains files, in a non-interactive context (or after answering 'no' at the prompt) without passing --yes.

Common situations: Re-running an install into a previously populated install dir; CI scripts hitting a stale directory from an earlier run; pointing install path at a shared or versioned directory that already has content.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at src/cli/install_into.rs:153

        // already exists at the install path. Check immediately before the
        // install performs that deletion (rather than at the start of `run`) so
        // a directory that became non-empty during tool resolution can't be
        // clobbered without an explicit opt-in. Refuse to overwrite a non-empty
        // directory (e.g. `.`) unless the user passes -y/--yes or confirms
        // interactively; the prompt defaults to "no" since it is destructive.
        // (#8115)
        if path_has_contents(&install_path) {
            let proceed = Settings::get().yes
                || prompt::confirm_with_default(
                    format!(
                        "{} is not empty; install-into will delete its contents. Continue?",
                        display_path(&install_path)
                    ),
                    false,
                )?
                .is_yes();
            if !proceed {
                bail!(
                    "refusing to overwrite non-empty directory {}; pass {} or choose an empty/new path",
                    display_path(&install_path),
                    style("--yes").yellow().for_stderr()
                );
            }
        }
        backend.install_version(install_ctx, tv).await?;
        Ok(())
    }
}

/// Replacement deletes the destination, so one that overlaps the lock directory
/// in either direction is refused: an ancestor would take the whole directory
/// with it, and a descendant could unlink an active lock file, letting the next
/// process create a replacement and install alongside the holder.
fn lock_dir_overlap(destination: &Path, lock_dir: &Path) -> eyre::Report {
    eyre!(
        "install-into destination {} overlaps mise's lock directory {}; choose a different destination",

View on GitHub (pinned to 533346cc37)