jdx/mise · error

managed path '{}' cannot be present while managed ancestor '

Error message

managed path '{}' cannot be present while managed ancestor '{}' is absent

What it means

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).

Source

Thrown at src/system/managed_files.rs:420

        validate_present_ancestors(&directory.path, directory.state, &directory_states)?;
    }
    Ok(())
}

fn validate_present_ancestors(
    path: &Path,
    state: ManagedState,
    directory_states: &std::collections::HashMap<&Path, ManagedState>,
) -> Result<()> {
    if state != ManagedState::Present {
        return Ok(());
    }
    if let Some(parent) = path
        .ancestors()
        .skip(1)
        .find(|parent| directory_states.get(parent) == Some(&ManagedState::Absent))
    {
        bail!(
            "managed path '{}' cannot be present while managed ancestor '{}' is absent",
            path.display(),
            parent.display()
        );
    }
    Ok(())
}

impl ManagedFileRequest {
    fn from_toml(
        root_config: &Config,
        path: PathBuf,
        config: ManagedFileTomlConfig,
        base: &Path,
        secrets: &super::secrets::SecretValues,
    ) -> Result<Self> {
        let owner = nonempty("owner", config.owner)?;
        let group = nonempty("group", config.group)?;

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Flip the ancestor directory to `state = "present"` if its contents should exist
  2. Move the present file/directory outside the absent ancestor's subtree (different path)
  3. Make the child `state = "absent"` too — removing the parent removes the child anyway, so an explicit absent child entry can be deleted entirely

Example fix

# before
[bootstrap.directories."/etc/legacy"]
state = "absent"
recursive = true
[bootstrap.files."/etc/legacy/keep.conf"]
content = "keep"

# after — keep.conf relocated out of the removed tree
[bootstrap.directories."/etc/legacy"]
state = "absent"
recursive = true
[bootstrap.files."/etc/keep.conf"]
content = "keep"
Defensive patterns

Strategy: validation

Validate before calling

// Reject present-under-absent-ancestor before planning
let absent_dirs: HashSet<_> = merged_dirs(config).iter().filter(|d| d.state == Absent).map(|d| d.path.clone()).collect();
for r in all_present_resources(config) {
    for ancestor in r.path.ancestors().skip(1) {
        assert!(!absent_dirs.contains(ancestor), "{} is present under absent ancestor {}", r.path.display(), ancestor.display());
    }
}

Prevention

When it happens

Trigger: `[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.

Common situations: 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.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/f6c23d4e6329e831. Report an issue: GitHub.