jdx/mise · error
refusing to remove non-directory path {} as a directory; dec
Error message
refusing to remove non-directory path {} as a directory; declare it in [bootstrap.files] What it means
Raised in ManagedDirectoryRequest::operation when a directory entry with state="absent" is planned and on-disk inspection returns ResourceAction::Unknown because the target path is actually a regular file (or other non-directory). The library refuses to delete a file through the directory resource; it must be declared under [bootstrap.files] instead.
Source
Thrown at src/system/managed_files.rs:668
group: nonempty("group", config.group)?,
mode: parse_mode(config.mode.as_deref(), 0o755)?,
state: config.state,
recursive: config.recursive,
replace: config.replace,
notify: config.notify,
origin,
inspection: None,
})
}
pub(crate) fn plan(&self) -> Result<ResourcePlan> {
plan_directory(self).map(|plan| plan.with_origin(self.origin.clone()))
}
fn operation(&self) -> Result<Option<PrivilegedAction>> {
match self.plan()?.action {
ResourceAction::Noop => return Ok(None),
ResourceAction::Unknown if self.state == ManagedState::Absent => bail!(
"refusing to remove non-directory path {} as a directory; declare it in [bootstrap.files]",
self.path.display()
),
ResourceAction::Unknown => bail!(
"refusing to replace non-directory path {}; set replace = true to allow replacement",
self.path.display()
),
_ => {}
}
Ok(Some(match self.state {
ManagedState::Present => PrivilegedAction::CreateDirectory {
path: self.path.clone(),
owner: self.owner.clone(),
group: self.group.clone(),
mode: self.mode,
replace: self.replace,
},
ManagedState::Absent => PrivilegedAction::RemoveDirectory {View on GitHub (pinned to afd2eddd3a)
Solutions
- Move the entry from [bootstrap.directories] to [bootstrap.files] with state = "absent" so it is removed as a file.
- Or delete the file manually and re-run bootstrap.
- If the path should exist, correct the entry's state/target rather than removing it.
Example fix
# before [[bootstrap.directories]] path = "/etc/myapp/legacy" state = "absent" # after [[bootstrap.files]] path = "/etc/myapp/legacy" state = "absent"
Defensive patterns
Strategy: validation
Validate before calling
for path in &declared_absent_dirs {
if let Ok(meta) = std::fs::metadata(path) {
if !meta.is_dir() {
eprintln!("{} is not a directory; declare under [bootstrap.files]", path.display());
}
}
} Type guard
fn is_directory(path: &std::path::Path) -> bool {
std::fs::metadata(path).map(|m| m.is_dir()).unwrap_or(true)
} Prevention
- Stat absent-declared directory paths on hosts to confirm they are actually directories.
- Keep the file/directory resource split consistent with what the path currently is on disk.
- After layout changes (package upgrades), re-audit absent entries.
When it happens
Trigger: Converging a [bootstrap.directories] entry with state = "absent" whose path exists on disk as a non-directory (plan_directory returns ResourceAction::Unknown), hitting the (Unknown, Absent) arm.
Common situations: An app replaced a former directory with a plain file (marker file, symlink change); the config author assumed the path was a directory; state drift where a previous bootstrap version wrote a file at that path.
Related errors
- refusing to remove directory {} as a file; declare it in [bo
- refusing to replace non-file path {}; set replace = true to
- refusing to replace non-directory path {}; set replace = tru
- refusing unsafe change to bootstrap user '{}'; inspect `mise
- refusing unsafe change to bootstrap compose project '{}'; in
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/b5076d761f8cd1ff.
Report an issue: GitHub.