{"record":{"id":"b5076d761f8cd1ff","repo":"jdx/mise","slug":"refusing-to-remove-non-directory-path-as-a-dire","errorCode":null,"errorMessage":"refusing to remove non-directory path {} as a directory; declare it in [bootstrap.files]","messagePattern":"refusing to remove non-directory path (.+?) as a directory; declare it in \\[bootstrap\\.files\\]","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/managed_files.rs","lineNumber":668,"sourceCode":"            group: nonempty(\"group\", config.group)?,\n            mode: parse_mode(config.mode.as_deref(), 0o755)?,\n            state: config.state,\n            recursive: config.recursive,\n            replace: config.replace,\n            notify: config.notify,\n            origin,\n            inspection: None,\n        })\n    }\n\n    pub(crate) fn plan(&self) -> Result<ResourcePlan> {\n        plan_directory(self).map(|plan| plan.with_origin(self.origin.clone()))\n    }\n\n    fn operation(&self) -> Result<Option<PrivilegedAction>> {\n        match self.plan()?.action {\n            ResourceAction::Noop => return Ok(None),\n            ResourceAction::Unknown if self.state == ManagedState::Absent => bail!(\n                \"refusing to remove non-directory path {} as a directory; declare it in [bootstrap.files]\",\n                self.path.display()\n            ),\n            ResourceAction::Unknown => bail!(\n                \"refusing to replace non-directory path {}; set replace = true to allow replacement\",\n                self.path.display()\n            ),\n            _ => {}\n        }\n        Ok(Some(match self.state {\n            ManagedState::Present => PrivilegedAction::CreateDirectory {\n                path: self.path.clone(),\n                owner: self.owner.clone(),\n                group: self.group.clone(),\n                mode: self.mode,\n                replace: self.replace,\n            },\n            ManagedState::Absent => PrivilegedAction::RemoveDirectory {","sourceCodeStart":650,"sourceCodeEnd":686,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/managed_files.rs#L650-L686","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\n[[bootstrap.directories]]\npath = \"/etc/myapp/legacy\"\nstate = \"absent\"\n\n# after\n[[bootstrap.files]]\npath = \"/etc/myapp/legacy\"\nstate = \"absent\"","handlingStrategy":"validation","validationCode":"for path in &declared_absent_dirs {\n    if let Ok(meta) = std::fs::metadata(path) {\n        if !meta.is_dir() {\n            eprintln!(\"{} is not a directory; declare under [bootstrap.files]\", path.display());\n        }\n    }\n}","typeGuard":"fn is_directory(path: &std::path::Path) -> bool {\n    std::fs::metadata(path).map(|m| m.is_dir()).unwrap_or(true)\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["filesystem","bootstrap","safety","path-type-mismatch"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}