jdx/mise · error · eyre::Report

refusing to manage the filesystem root

Error message

refusing to manage the filesystem root

What it means

After absolutizing, a managed path equal to "/" is rejected because managing (creating, replacing, or deleting) the filesystem root is always destructive and never a legitimate declaration. `validate_privileged_target` blocks it explicitly as a safety rail.

Solutions

  1. Correct the path to the specific file or directory actually intended (e.g. "/etc/myapp").
  2. Check any variables used in the path — one of them is expanding to "/" or empty; give it a real value.
  3. If the entry is leftover scaffolding, remove it from the config entirely.

Example fix

# before
[bootstrap.directories.appdata]
path = "{{base_dir}}"   # base_dir resolves to "/"

# after
[bootstrap.directories.appdata]
path = "/var/lib/myapp"
Defensive patterns

Strategy: validation

Validate before calling

def assert_not_root(p):
    import os
    r = os.path.realpath(os.path.expanduser(p))
    if r == '/':
        raise ValueError(f"refusing to manage filesystem root: {p}")
    return r

Prevention

When it happens

Trigger: A [bootstrap.files]/[bootstrap.directories] entry whose path resolves to "/" — either literally, via a variable that expands to empty/root, or via `replace_path` substitution producing "/".

Common situations: A template variable like `${base_dir}` left unset or set to "/"; typo where the final path component was dropped ("/etc/myapp/" mishandled by tooling); experimentation gone wrong.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/73ab44503d3f6d1a. Report an issue: GitHub.

Appendix: source

Thrown at src/system/managed_files.rs:1375

            Self::Symlink
        } else {
            Self::Other
        }
    }
}

fn absolute_target(path: &str) -> Result<PathBuf> {
    let path = crate::file::replace_path(Path::new(path));
    validate_privileged_target(&path)
}

fn validate_privileged_target(path: &Path) -> Result<PathBuf> {
    if !path.is_absolute() {
        bail!("managed system path must be absolute: {}", path.display());
    }
    let path = path.absolutize()?.to_path_buf();
    if path == Path::new("/") {
        bail!("refusing to manage the filesystem root");
    }
    Ok(path)
}

fn parse_mode(mode: Option<&str>, default: u32) -> Result<u32> {
    let Some(mode) = mode else {
        return Ok(default);
    };
    let mode = mode.strip_prefix("0o").unwrap_or(mode);
    let parsed = u32::from_str_radix(mode, 8).wrap_err("mode must be an octal string")?;
    if parsed > 0o7777 {
        bail!("mode must be between 0000 and 7777");
    }
    Ok(parsed)
}

fn nonempty(field: &str, value: Option<String>) -> Result<Option<String>> {
    match value {

View on GitHub (pinned to 533346cc37)