{"record":{"id":"73ab44503d3f6d1a","repo":"jdx/mise","slug":"refusing-to-manage-the-filesystem-root","errorCode":null,"errorMessage":"refusing to manage the filesystem root","messagePattern":"refusing to manage the filesystem root","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/managed_files.rs","lineNumber":1198,"sourceCode":"            Self::Symlink\n        } else {\n            Self::Other\n        }\n    }\n}\n\nfn absolute_target(path: &str) -> Result<PathBuf> {\n    let path = crate::file::replace_path(Path::new(path));\n    validate_privileged_target(&path)\n}\n\nfn validate_privileged_target(path: &Path) -> Result<PathBuf> {\n    if !path.is_absolute() {\n        bail!(\"managed system path must be absolute: {}\", path.display());\n    }\n    let path = path.absolutize()?.to_path_buf();\n    if path == Path::new(\"/\") {\n        bail!(\"refusing to manage the filesystem root\");\n    }\n    Ok(path)\n}\n\nfn parse_mode(mode: Option<&str>, default: u32) -> Result<u32> {\n    let Some(mode) = mode else {\n        return Ok(default);\n    };\n    let mode = mode.strip_prefix(\"0o\").unwrap_or(mode);\n    let parsed = u32::from_str_radix(mode, 8).wrap_err(\"mode must be an octal string\")?;\n    if parsed > 0o7777 {\n        bail!(\"mode must be between 0000 and 7777\");\n    }\n    Ok(parsed)\n}\n\nfn nonempty(field: &str, value: Option<String>) -> Result<Option<String>> {\n    match value {","sourceCodeStart":1180,"sourceCodeEnd":1216,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/managed_files.rs#L1180-L1216","documentation":"After absolutizing the target, validate_privileged_target refuses the exact path \"/\". Managing the filesystem root would mean chowning/chmodding root itself and cascading over everything, so mise hard-stops. Only the canonical root path is refused; any deeper path is allowed.","triggerScenarios":"A [bootstrap.files] or [bootstrap.directories] entry whose key is \"/\" or normalizes to it (e.g. \"/..\").","commonSituations":"A templating loop or variable renders an empty/root path into the config; hand-edited typo; generated config not reviewed before bootstrap.","solutions":["Point the entry at a real target under root, e.g. \"/opt/app\"","If a template produced \"/\", fix the variable (empty suffix, missing path join) and regenerate"],"exampleFix":"# before\n[bootstrap.directories]\n\"/\" = { mode = \"0755\" }\n\n# after\n[bootstrap.directories]\n\"/opt/app\" = { owner = \"app\", mode = \"0755\" }","handlingStrategy":"validation","validationCode":"use path_absolutize::Absolutize;\nif let Ok(canon) = std::path::Path::new(key).absolutize() {\n    if canon == std::path::Path::new(\"/\") {\n        return Err(eyre::eyre!(\"refusing to manage the filesystem root\"));\n    }\n}","typeGuard":"fn is_manageable_target(p: &std::path::Path) -> bool {\n    p.is_absolute() && p != std::path::Path::new(\"/\")\n}","tryCatchPattern":null,"preventionTips":["Never template the root or an empty string into a path key","Review generated mise.toml files before running bootstrap"],"tags":["safety","path","validation","managed-files","guard"],"backgroundTag":"filesystem-root-guard","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}