{"record":{"id":"fefaea9852898401","repo":"jdx/mise","slug":"refusing-to-remove-non-directory-path","errorCode":null,"errorMessage":"refusing to remove non-directory path: {}","messagePattern":"refusing to remove non-directory path: (.+?)","errorType":"error_code","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/managed_files.rs","lineNumber":1797,"sourceCode":"        (0o0020, nix::sys::stat::Mode::S_IWGRP),\n        (0o0010, nix::sys::stat::Mode::S_IXGRP),\n        (0o0004, nix::sys::stat::Mode::S_IROTH),\n        (0o0002, nix::sys::stat::Mode::S_IWOTH),\n        (0o0001, nix::sys::stat::Mode::S_IXOTH),\n    ]\n    .into_iter()\n    .filter_map(|(bit, flag)| (mode & bit != 0).then_some(flag))\n    .fold(nix::sys::stat::Mode::empty(), |mode, flag| mode | flag);\n    nix::sys::stat::fchmod(directory, platform_mode)?;\n    Ok(())\n}\n\nfn remove_directory(path: &Path, recursive: bool) -> Result<()> {\n    match fs::symlink_metadata(path) {\n        Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(()),\n        Err(err) => Err(err.into()),\n        Ok(metadata) if !metadata.file_type().is_dir() => {\n            bail!(\"refusing to remove non-directory path: {}\", path.display())\n        }\n        Ok(_) if recursive => fs::remove_dir_all(path).map_err(Into::into),\n        Ok(_) => fs::remove_dir(path).map_err(Into::into),\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn file(path: &str, state: ManagedState) -> ManagedFileRequest {\n        ManagedFileRequest {\n            phase: ManagedFilePhase::default(),\n            path: PathBuf::from(path),\n            content: (state == ManagedState::Present).then(|| \"content\".to_string()),\n            owner: None,\n            group: None,\n            mode: 0o644,","sourceCodeStart":1779,"sourceCodeEnd":1815,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/managed_files.rs#L1779-L1815","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n$ ls ~/.local/share/mise/installs/node\ntextfile  # a file where a directory is expected\n// after\nrm ~/.local/share/mise/installs/node/textfile && mkdir ~/.local/share/mise/installs/node","handlingStrategy":"validation","validationCode":"import os\ndef is_real_directory(p):\n    return os.path.isdir(p) and not os.path.islink(p)","typeGuard":"def is_real_dir(p):\n    const st = fs.lstatSync(p);\n    return st.isDirectory();","tryCatchPattern":"match remove_dir(path, recursive) {\n    Err(e) if e.to_string().starts_with(\"refusing to remove non-directory\") => {\n        // inspect path: file or symlink found where a directory is expected\n    }\n    r => r,\n}","preventionTips":["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"],"tags":["filesystem","path-type","safety"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}