jdx/mise · error
: ' ' must be a safe relative path (no absolute paths or…
Error message
{option}: '{path}' must be a safe relative path (no absolute paths or parent directories) What it means
mise rejects configured binary paths that are absolute or contain parent-directory components. Unlike the plain-name check, safe relative paths like "bin/tool" are still allowed. ensure_safe_relative_bin_path prevents a config from escaping the install directory via an absolute path or '..'.
Solutions
- Rewrite the value as a path relative to the install directory without '..' components (e.g. 'bin/tool')
- Remove any leading '/' or drive letter
- Re-check after templating; ensure templates resolve to relative paths
Example fix
// before
ensure_safe_relative_bin_path("bin", "/opt/tools/bin/mytool")?;
// after
ensure_safe_relative_bin_path("bin", "bin/mytool")?; Defensive patterns
Strategy: validation
Validate before calling
use std::path::{Path, Component};
fn assert_safe_relative(path: &str) -> Result<(), String> {
let p = Path::new(path);
if p.is_absolute() || p.components().any(|c| c == Component::ParentDir) {
return Err(format!("unsafe relative path: {path}"));
}
Ok(())
} Type guard
fn is_safe_relative(path: &str) -> bool {
!path.starts_with('/') && !Path::new(path).components().any(|c| c == std::path::Component::ParentDir)
} Prevention
- Express all binary paths relative to the install dir
- Reject absolute paths in config review
- Avoid '..' components entirely
When it happens
Trigger: Passing an absolute path ('/usr/bin/tool') or a path with '..' ('../tool', 'a/../../tool') to dest_filename, install_artifact, make_configured_bin_executable, or rename_executable_in_dir via a path-like config option.
Common situations: Users copying absolute paths from their own filesystem into tool config; nested relative paths that accidentally traverse upward after a config refactor.
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
- : ' ' must be a plain file name (no path separators or…
- agent ' ' `start_calendar_interval. ` must be between and
- agent ' ' `start_calendar_interval` must set at least one…
- agent ' ' `start_calendar_interval` must not be empty
- agent ' ' `queue_directories` must not contain empty entries
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/d00cb70fd5d1ee77.
Report an issue: GitHub.
Appendix: source
Thrown at src/backend/static_helpers.rs:1210
/// 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(crate) 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(crate) 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 533346cc37)