jdx/mise · error · eyre::Report

{option}: '{path}' must be a safe relative path (no absolute

Error message

{option}: '{path}' must be a safe relative path (no absolute paths or parent directories)

What it means

mise validates the `bin` (and `rename target`) option with `file::is_safe_relative_path`: it must be a non-empty relative path whose components are all normal (no leading `/`, no `C:` drive prefix, no `..` segments). This preserves the accepted `bin = "bin/tool"` form while rejecting absolute or parent-escaping paths that would resolve the binary outside the install directory. Thrown at option-parse time in static backends (src/backend/static_helpers.rs:587, :794, :900; src/backend/http.rs:405).

Source

Thrown at src/backend/static_helpers.rs:1145

/// Rejects `bin`/`rename_exe` names that are not plain file names (`../tool`,
/// `/abs/tool`, `bin/tool`), which would otherwise be joined onto the install
/// or search directory and place the binary outside it.
pub fn ensure_plain_bin_name(option: &str, name: &str) -> eyre::Result<()> {
    if !file::is_plain_file_name(name) {
        bail!(
            "{option}: '{name}' must be a plain file name \
             (no path separators or parent directories)"
        );
    }
    Ok(())
}

/// Rejects a configured binary path that is absolute or contains parent
/// components, while preserving the established `bin = "bin/tool"` form.
pub fn ensure_safe_relative_bin_path(option: &str, path: &str) -> eyre::Result<()> {
    if !file::is_safe_relative_path(path) {
        bail!(
            "{option}: '{path}' must be a safe relative path \
             (no absolute paths or parent directories)"
        );
    }
    Ok(())
}

/// Renames `path` (named `file_name`) to `target` within `dir`, preserving any
/// required extension and ensuring the result is executable. A collision on the
/// target (two mappings pointing at the same name, or the archive already
/// containing that name) is unsatisfiable, so it fails the install loudly rather
/// than silently dropping a binary and reporting success.
fn finish_rename(dir: &Path, path: &Path, file_name: &str, target: &str) -> eyre::Result<()> {
    let target_path = keep_required_extensions(dir, file_name, target, dir.join(target));
    // Ensure the binary is executable whether or not we move it: ZIP archives drop
    // the exec bit, and the file may already carry the desired name.
    if !file::is_executable(path) {
        file::make_executable(path)?;

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Make the value relative to the tool's install directory, e.g. bin = "bin/tool" or bin = "tool"
  2. Remove leading slashes/backslashes and drive prefixes; replace `..` segments with the real relative location inside the archive
  3. If you genuinely need an absolute path to an external binary, that is not what `bin` does — reference the executable via shims or PATH instead

Example fix

# before (mise.toml)
[tools.http]
mytool = { url = 'https://example.com/t.tar.gz', bin = '/usr/local/bin/mytool' }

# after
[tools.http]
mytool = { url = 'https://example.com/t.tar.gz', bin = 'bin/mytool' }
Defensive patterns

Strategy: validation

Validate before calling

# Rust: mirror of file::is_safe_relative_path
fn is_safe_relative_bin(s: &str) -> bool {
    if s.is_empty() { return false; }
    let n = s.replace('\\', "/");
    let b = n.as_bytes();
    if n.starts_with('/') || (b.len() >= 2 && b[0].is_ascii_alphabetic() && b[1] == b':') {
        return false;
    }
    n.components().all(|c| matches!(c, std::path::Component::Normal(_)))
}
assert!(is_safe_relative_bin("bin/tool"));
assert!(!is_safe_relative_bin("../tool"));

Prevention

When it happens

Trigger: Setting `bin = "/usr/local/bin/tool"`, `bin = "../../usr/bin/tool"`, `bin = "C:\\tools\\tool.exe"`, or `bin = ""` in a mise.toml tool entry backed by the http/ubi/static helpers. Any of these makes ensure_safe_relative_bin_path bail before the install proceeds.

Common situations: Users pasting an absolute system path into `bin` thinking it is the target location on disk; configs using Windows drive letters; values with trailing `..` from manually trimming a prefix; empty string left after template rendering.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


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