jdx/mise · error

managed system path '{}' is declared as both a file and a di

Error message

managed system path '{}' is declared as both a file and a directory

What it means

During planning of `[bootstrap.files]` / `[bootstrap.directories]`, a file entry's construction failed with a secret-unavailable error (its template references a secret whose env var/keychain value is missing, detected via secrets::is_unavailable downcasting to SecretUnavailable), AND the same normalized path is also declared under `[bootstrap.directories]`. Because the file couldn't be inspected, mise can't reconcile the file/directory overlap safely, so it bails instead of guessing (managed_files.rs:207-213).

Source

Thrown at src/system/managed_files.rs:209

) -> Result<(
    Vec<ManagedFileRequest>,
    Vec<ManagedDirectoryRequest>,
    Vec<ResourcePlan>,
)> {
    let mut files = vec![];
    let mut unavailable = vec![];
    let mut directories = directories_from_config(config)?;
    let directory_states = directories
        .iter()
        .map(|directory| (directory.path.as_path(), directory.state))
        .collect::<std::collections::HashMap<_, _>>();
    for (path, (file, base)) in merged_files_from_config(config)? {
        let state = file.state;
        match ManagedFileRequest::from_toml(config, path.clone(), file, &base, secrets) {
            Ok(file) => files.push(file),
            Err(error) if super::secrets::is_unavailable(&error) => {
                if directory_states.contains_key(path.as_path()) {
                    bail!(
                        "managed system path '{}' is declared as both a file and a directory",
                        path.display()
                    );
                }
                validate_present_ancestors(&path, state, &directory_states)?;
                unavailable.push(ResourcePlan::new(
                    ResourceId::new("file", path.to_string_lossy().into_owned()),
                    "not inspected: required secret unavailable",
                    "template rendered",
                    ResourceAction::Unknown,
                ));
            }
            Err(error) => return Err(error),
        }
    }
    ignore_non_linux_account_principals(config, &mut files, &mut directories);
    validate_requests(&files, &directories)?;
    inspect_paths(&mut files, &mut directories)?;

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Remove one of the two declarations for the same path — a path must be either a file or a directory, never both
  2. If you intended to migrate, delete the old `[bootstrap.files."<path>"]` block entirely and keep the directory entry (or the reverse)
  3. Provision the missing secret (set the env var / keychain entry) so the file request constructs normally — though the duplicate will then be caught by validate_requests anyway, so the declaration conflict must be fixed regardless
  4. Check every layered mise.toml (project, ~/.config/mise, MISE_CONFIG_FILE) for a stale duplicate

Example fix

# before
[bootstrap.files."/etc/myapp"]
content = "{{ secrets.token }}"
template = true

[bootstrap.directories."/etc/myapp"]
owner = "root"

# after
[bootstrap.directories."/etc/myapp"]
owner = "root"
Defensive patterns

Strategy: validation

Validate before calling

# Validate before running bootstrap: no path in both tables (any layer), and secrets set for templated files
use std::collections::HashSet;
let dirs: HashSet<_> = layers().iter().flat_map(|c| c.bootstrap.directories.keys().map(normalize)).collect();
for c in layers() {
    for path in c.bootstrap.files.keys() {
        assert!(!dirs.contains(&normalize(path)), "path {path} declared as both file and directory");
    }
}
for f in templated_files() { ensure_secret_available(f)?; } // fail loudly before planning

Prevention

When it happens

Trigger: A mise.toml with both `[bootstrap.files."/etc/app"]` (templated, whose required secret env var is unset) and `[bootstrap.directories."/etc/app"]`. Normally the duplicate-path check at validate_requests (line 394) catches overlaps, but the secret-unavailable branch runs earlier during request construction, so it has its own copy of the conflict check before planning a 'not inspected' placeholder.

Common situations: Refactoring config and moving a path from files to directories (or vice versa) while the old block is left behind; templated file whose secret wasn't provisioned on a new machine, exposing the latent duplicate declaration; layered config files (project + ~/.config/mise) each declaring the same path in different tables.

Related errors


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