{"record":{"id":"843ba1d9e975218d","repo":"jdx/mise","slug":"managed-file-parent-is-not-a-directory","errorCode":null,"errorMessage":"managed file parent is not a directory: {}","messagePattern":"managed file parent is not a directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/managed_files.rs","lineNumber":1356,"sourceCode":"    _mode: u32,\n) -> Result<()> {\n    bail!(\"managed system files are only supported on Unix\")\n}\n\nfn write_file(\n    path: &Path,\n    content: &[u8],\n    owner: Option<&str>,\n    group: Option<&str>,\n    mode: u32,\n    replace: bool,\n) -> Result<()> {\n    let parent = path\n        .parent()\n        .ok_or_else(|| eyre!(\"managed file has no parent: {}\", path.display()))?;\n    match fs::metadata(parent) {\n        Ok(metadata) if metadata.is_dir() => {}\n        Ok(_) => bail!(\n            \"managed file parent is not a directory: {}\",\n            parent.display()\n        ),\n        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {\n            bail!(\"managed file parent does not exist: {}\", parent.display())\n        }\n        Err(error) => return Err(error.into()),\n    }\n    // Prepare the complete replacement before mutating the destination. In\n    // particular, a metadata permission error must leave the old path intact.\n    let mut temporary = tempfile::NamedTempFile::new_in(parent)?;\n    temporary.write_all(content)?;\n    set_metadata(temporary.path(), owner, group, mode)?;\n    temporary.as_file_mut().sync_all()?;\n    match fs::symlink_metadata(path) {\n        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}\n        Err(error) => return Err(error.into()),\n        Ok(metadata) if metadata.file_type().is_file() => {}","sourceCodeStart":1338,"sourceCodeEnd":1374,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/managed_files.rs#L1338-L1374","documentation":"write_file() requires the managed file's parent directory to already exist and be a directory. fs::metadata(parent) succeeded but reported a non-directory (regular file, symlink to a file, etc.), so mise refuses to clobber something occupying the would-be parent path before writing the target.","triggerScenarios":"Managing /opt/app/settings.conf while /opt/app exists as a regular file; any case where the literal parent component of the managed path is an existing non-directory.","commonSituations":"A leftover file where a directory was expected (e.g. /etc/app was once a file); a path typo that collides with an existing file.","solutions":["Remove or rename the file occupying the parent path, then re-run bootstrap","Manage the conflicting file as absent first ([bootstrap.files] entry with state = \"absent\"), then declare the directory and the file","Fix a mistyped path that collides with an existing file"],"exampleFix":"# before: a regular file exists at /srv/app, blocking the parent check\n[bootstrap.files]\n\"/srv/app/settings.conf\" = { content = \"...\" }\n\n# after: delete the conflicting file once (rm /srv/app), then manage parent + file\n[bootstrap.directories]\n\"/srv/app\" = { mode = \"0755\" }\n[bootstrap.files]\n\"/srv/app/settings.conf\" = { content = \"...\", mode = \"0644\" }","handlingStrategy":"validation","validationCode":"let parent = path.parent().ok_or_else(|| eyre::eyre!(\"no parent\"))?;\nmatch std::fs::metadata(parent) {\n    Ok(m) if m.is_dir() => Ok(()),\n    Ok(_) => Err(eyre::eyre!(\"parent is not a directory: {}\", parent.display())),\n    Err(e) => Err(e.into()),\n}","typeGuard":"fn parent_is_directory(path: &std::path::Path) -> bool {\n    path.parent()\n        .and_then(|p| std::fs::metadata(p).ok())\n        .map(|m| m.is_dir())\n        .unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Check parent paths before declaring nested managed files","Prefer declaring the parent as a managed directory so the run is self-contained"],"tags":["filesystem","path","validation","managed-files"],"backgroundTag":"parent-not-a-directory","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}