jdx/mise · error · eyre::Report

refusing to remove non-directory path

Error message

refusing to remove non-directory path: {}

What it means

remove_directory only accepts real directories. If the path exists but is not a directory (per symlink_metadata, so a symlink to a directory also fails), mise refuses to remove it instead of silently deleting a file or following a symlink.

Solutions

  1. Inspect the path (ls -la) and replace the file/symlink with a real directory if that is intended
  2. Remove the offending file manually if it is stale, then re-run the mise command
  3. Point mise at the correct directory path

Example fix

// before
$ ls ~/.local/share/mise/installs/node
textfile  # a file where a directory is expected
// after
rm ~/.local/share/mise/installs/node/textfile && mkdir ~/.local/share/mise/installs/node
Defensive patterns

Strategy: validation

Validate before calling

import os
def is_real_directory(p):
    return os.path.isdir(p) and not os.path.islink(p)

Type guard

def is_real_dir(p):
    const st = fs.lstatSync(p);
    return st.isDirectory();

Try / catch

match remove_dir(path, recursive) {
    Err(e) if e.to_string().starts_with("refusing to remove non-directory") => {
        // inspect path: file or symlink found where a directory is expected
    }
    r => r,
}

Prevention

When it happens

Trigger: Calling mise's managed-file removal (remove_directory) on a path that is a regular file, symlink, or other non-directory; the path was deleted and replaced by a file; recursive flag irrelevant since the type check comes first.

Common situations: A managed directory was overwritten by a plain file by another tool; a symlink was placed where mise expected a directory; stale/corrupted state in mise's data directories.

Related errors


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

Appendix: source

Thrown at src/system/managed_files.rs:1797

        (0o0020, nix::sys::stat::Mode::S_IWGRP),
        (0o0010, nix::sys::stat::Mode::S_IXGRP),
        (0o0004, nix::sys::stat::Mode::S_IROTH),
        (0o0002, nix::sys::stat::Mode::S_IWOTH),
        (0o0001, nix::sys::stat::Mode::S_IXOTH),
    ]
    .into_iter()
    .filter_map(|(bit, flag)| (mode & bit != 0).then_some(flag))
    .fold(nix::sys::stat::Mode::empty(), |mode, flag| mode | flag);
    nix::sys::stat::fchmod(directory, platform_mode)?;
    Ok(())
}

fn remove_directory(path: &Path, recursive: bool) -> Result<()> {
    match fs::symlink_metadata(path) {
        Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),
        Err(err) => Err(err.into()),
        Ok(metadata) if !metadata.file_type().is_dir() => {
            bail!("refusing to remove non-directory path: {}", path.display())
        }
        Ok(_) if recursive => fs::remove_dir_all(path).map_err(Into::into),
        Ok(_) => fs::remove_dir(path).map_err(Into::into),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn file(path: &str, state: ManagedState) -> ManagedFileRequest {
        ManagedFileRequest {
            phase: ManagedFilePhase::default(),
            path: PathBuf::from(path),
            content: (state == ManagedState::Present).then(|| "content".to_string()),
            owner: None,
            group: None,
            mode: 0o644,

View on GitHub (pinned to 533346cc37)