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
- Inspect the path (ls -la) and replace the file/symlink with a real directory if that is intended
- Remove the offending file manually if it is stale, then re-run the mise command
- 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
- Check the path with ls -la before removal operations
- Replace stale files/symlinks at managed paths with real directories
- Avoid letting other tools write files into mise's install directories
- Verify state after crashed installs or interrupted rm -rf
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
- changed during recovery; left untouched
- refusing to replace non-file path
- refusing to resolve managed directory
- a parent directory is now a symlink; left untouched
- app Info.plist must be a regular file
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)