{"record":{"id":"f6c23d4e6329e831","repo":"jdx/mise","slug":"managed-path-cannot-be-present-while-managed","errorCode":null,"errorMessage":"managed path '{}' cannot be present while managed ancestor '{}' is absent","messagePattern":"managed path '(.+?)' cannot be present while managed ancestor '(.+?)' is absent","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/managed_files.rs","lineNumber":420,"sourceCode":"        validate_present_ancestors(&directory.path, directory.state, &directory_states)?;\n    }\n    Ok(())\n}\n\nfn validate_present_ancestors(\n    path: &Path,\n    state: ManagedState,\n    directory_states: &std::collections::HashMap<&Path, ManagedState>,\n) -> Result<()> {\n    if state != ManagedState::Present {\n        return Ok(());\n    }\n    if let Some(parent) = path\n        .ancestors()\n        .skip(1)\n        .find(|parent| directory_states.get(parent) == Some(&ManagedState::Absent))\n    {\n        bail!(\n            \"managed path '{}' cannot be present while managed ancestor '{}' is absent\",\n            path.display(),\n            parent.display()\n        );\n    }\n    Ok(())\n}\n\nimpl ManagedFileRequest {\n    fn from_toml(\n        root_config: &Config,\n        path: PathBuf,\n        config: ManagedFileTomlConfig,\n        base: &Path,\n        secrets: &super::secrets::SecretValues,\n    ) -> Result<Self> {\n        let owner = nonempty(\"owner\", config.owner)?;\n        let group = nonempty(\"group\", config.group)?;","sourceCodeStart":402,"sourceCodeEnd":438,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/managed_files.rs#L402-L438","documentation":"validate_present_ancestors (managed_files.rs:413-427) enforces state consistency up the directory tree: a resource declared `state = \"present\"` may not have any managed ancestor directory declared `state = \"absent\"`. It scans path.ancestors() (skipping the path itself) against the directory_states map and bails on the first absent ancestor. This runs for every file and directory request during validate_requests and in the secret-unavailable branch (603).","triggerScenarios":"`[bootstrap.directories.\"/etc/legacy\"] state = \"absent\"` together with `[bootstrap.files.\"/etc/legacy/app.conf\"] state = \"present\"`; or absent directory `/opt/stack` with present directory `/opt/stack/sub`. Any present path whose parent, grandparent, ... is a managed-absent directory errors — even when the absent ancestor is several levels up.","commonSituations":"Cleaning up a tree with absent while keeping one config file inside it; layered configs where one layer removes a directory another layer populates; ordering assumptions ('the file is written before the directory is removed') — mise does not order around this, it forbids it.","solutions":["Flip the ancestor directory to `state = \"present\"` if its contents should exist","Move the present file/directory outside the absent ancestor's subtree (different path)","Make the child `state = \"absent\"` too — removing the parent removes the child anyway, so an explicit absent child entry can be deleted entirely"],"exampleFix":"# before\n[bootstrap.directories.\"/etc/legacy\"]\nstate = \"absent\"\nrecursive = true\n[bootstrap.files.\"/etc/legacy/keep.conf\"]\ncontent = \"keep\"\n\n# after — keep.conf relocated out of the removed tree\n[bootstrap.directories.\"/etc/legacy\"]\nstate = \"absent\"\nrecursive = true\n[bootstrap.files.\"/etc/keep.conf\"]\ncontent = \"keep\"","handlingStrategy":"validation","validationCode":"// Reject present-under-absent-ancestor before planning\nlet absent_dirs: HashSet<_> = merged_dirs(config).iter().filter(|d| d.state == Absent).map(|d| d.path.clone()).collect();\nfor r in all_present_resources(config) {\n    for ancestor in r.path.ancestors().skip(1) {\n        assert!(!absent_dirs.contains(ancestor), \"{} is present under absent ancestor {}\", r.path.display(), ancestor.display());\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Before marking a directory absent, sweep every config layer for present children under it","Removing a parent makes explicit child removals redundant — delete child entries when the tree goes","Keep remove-trees and keep-files in disjoint subtrees from the start"],"tags":["bootstrap","config-validation","state-conflict","managed-files"],"backgroundTag":"config-state-conflict","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}