jdx/mise · error
invalid tool version
Error message
invalid tool version {s:?}: contains path-traversal sequence What it means
mise rejects tool version strings containing the '..' path-traversal sequence. Versions are used to build install paths, so a value like '../../etc' could escape the install directory. The check runs in validate_version_string during ToolRequest construction.
Solutions
- Remove the '..' sequence and supply a real version identifier.
- If a path was intended, use the path: prefix (path:/some/dir) instead of a version.
- Audit generated configs/scripts that interpolate values into version positions.
Example fix
// mise.toml before node = "../shared/node" // after node = "path:../shared/node" # or a concrete version like "20.11.0"
Defensive patterns
Strategy: validation
Validate before calling
function validVersion(v) {
return typeof v === 'string' && !v.includes('..');
} Prevention
- Keep paths and versions in separate fields; use path: for directories.
- Lint mise.toml for '..' in version values.
- Sanitize generated configs that interpolate user input.
When it happens
Trigger: Passing a version containing '..' (e.g. `mise install foo@..`, foo@1.2..3) through new_with_options or equivalent CLI/config input.
Common situations: Path accidentally pasted as a version; scripted substitution injecting a relative path; typo'd ellipsis or range syntax in mise.toml [tools] entries.
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
- invalid tool ref : contains path-traversal sequence
- brew-cask: completion target
- brew-cask: invalid
- brew-cask: invalid structured flight path
- remote cache path escapes its output root
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/28fb83bf9b6b26b9.
Report an issue: GitHub.
Appendix: source
Thrown at src/toolset/tool_request.rs:721
/// The deny list is the minimum set of characters that can break out of either
/// a single- or double-quoted shell string, or that trigger expansion *inside*
/// double quotes: quotes themselves, backslash, backtick, and `$`. Plus control
/// characters (newlines split shell tokens) and `..` (filesystem traversal).
/// Everything else is allowed so legitimate version vocabulary (npm-style
/// semver ranges like `>=20 <21 || >=22` or `^1.0.0`, dates, channel names,
/// `lts/hydrogen`, etc.) continues to work — those characters are only
/// dangerous in *unquoted* shell context, which cannot occur without one of
/// the rejected expansion characters appearing first. Leading dashes are also
/// rejected so backend install tools cannot mistake a version for a CLI flag.
fn validate_version_string(s: &str) -> Result<()> {
if s.is_empty() {
return Ok(());
}
if s.starts_with('-') {
bail!("invalid tool version {s:?}: must not start with '-'");
}
if s.contains("..") {
bail!("invalid tool version {s:?}: contains path-traversal sequence");
}
if let Some(c) = s.chars().find(|c| is_forbidden_version_char(*c)) {
bail!("invalid tool version {s:?}: contains forbidden character {c:?}");
}
Ok(())
}
/// Validate `ref:`/`branch:`/`tag:`/`rev:` values. Same character rules as
/// version strings: branch/tag names already use the same broad vocabulary
/// (`/`, `+`, `-`, etc.), so only shell-quote-breaking characters and leading
/// dashes need rejection. Kept as a separate function for distinct error
/// messages.
fn validate_ref_string(s: &str) -> Result<()> {
if s.is_empty() {
return Ok(());
}
if s.starts_with('-') {
bail!("invalid tool ref {s:?}: must not start with '-'");View on GitHub (pinned to 533346cc37)