jdx/mise · error
refusing to remove directory as a file: {}
Error message
refusing to remove directory as a file: {} What it means
For a managed file with state = "absent", remove_file() refuses when the path currently exists but is a directory. Removing it would require directory semantics (and possibly recursion) that the file entry does not declare, so the resource type and on-disk type must agree.
Source
Thrown at src/system/managed_files.rs:1401
)
})?
}
Ok(_) => fs::remove_file(path)?,
}
temporary
.persist(path)
.map_err(|error| error.error)
.wrap_err_with(|| format!("failed to atomically replace {}", path.display()))?;
fs::File::open(parent)?.sync_all()?;
Ok(())
}
fn remove_file(path: &Path) -> 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 directory as a file: {}", path.display())
}
Ok(_) => fs::remove_file(path).map_err(Into::into),
}
}
fn create_directory(
path: &Path,
owner: Option<&str>,
group: Option<&str>,
mode: u32,
replace: bool,
) -> Result<()> {
match fs::symlink_metadata(path) {
Err(error) if error.kind() == std::io::ErrorKind::NotFound => fs::create_dir(path)?,
Err(error) => return Err(error.into()),
Ok(metadata) if metadata.file_type().is_dir() => {}
Ok(_) if !replace => {
bail!("refusing to replace non-directory path: {}", path.display())View on GitHub (pinned to 9dcfcaa0dc)
Solutions
- Declare the removal under [bootstrap.directories] with state = "absent" (and recursive = true if it may be non-empty)
- Or remove the directory manually (rmdir / rm -r) and let the file entry find nothing to do
Example fix
# before: /opt/app/legacy.conf is actually a directory
[bootstrap.files]
"/opt/app/legacy.conf" = { state = "absent" }
# after
[bootstrap.directories]
"/opt/app/legacy.conf" = { state = "absent", recursive = true } Defensive patterns
Strategy: validation
Validate before calling
if state == ManagedState::Absent {
if let Ok(m) = std::fs::symlink_metadata(path) {
if m.file_type().is_dir() {
return Err(eyre::eyre!("path is a directory; declare it under [bootstrap.directories]"));
}
}
} Type guard
fn removal_kind_matches(path: &std::path::Path, expect_dir: bool) -> bool {
match std::fs::symlink_metadata(path) {
Err(_) => true,
Ok(m) => m.file_type().is_dir() == expect_dir,
}
} Prevention
- Match the resource table (files vs directories) to what actually sits on disk
- Re-check absent declarations after restructuring paths
When it happens
Trigger: A [bootstrap.files] entry with state = "absent" whose path is actually a directory on disk.
Common situations: A path changed roles over time (was a file, later became a directory); stale absent declarations after restructuring config.
Related errors
- refusing to remove non-directory path: {}
- refusing to replace non-directory path: {}
- managed file parent is not a directory: {}
- managed file parent does not exist: {}
- refusing to replace non-file path: {}
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/1394bc36f31e5170.
Report an issue: GitHub.