jdx/mise · error
invalid tool version {s:?}: must not start with '-'
Error message
invalid tool version {s:?}: must not start with '-' What it means
validate_version_string rejects version strings that could be mistaken for CLI flags. A version starting with '-' would be parsed as an option by backend install tools (cargo, go, etc.), so mise rejects it up front with this error.
Source
Thrown at src/toolset/tool_request.rs:628
/// install path names and (for vfox plugins) into `ctx.version` / `ctx.rootPath`
/// values that downstream Lua hooks often interpolate into shell commands.
///
/// 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(());View on GitHub (pinned to afd2eddd3a)
Solutions
- Remove the leading '-' from the version string
- Fix the script/variable interpolation producing the empty or flag-like version
- Use a concrete version or valid channel like 'latest'
- Quote and validate user input before embedding it in tool@version strings
Example fix
// before node = '--latest' // after node = 'latest'
Defensive patterns
Strategy: validation
Validate before calling
// reject flag-like versions before calling mise
if (typeof version === 'string' && version.startsWith('-')) throw new Error('version must not start with -'); Type guard
const safeVersion = (v) => typeof v === 'string' && v.length > 0 && !v.startsWith('-') && !v.includes('..'); Try / catch
try { installTool(name, version) } catch (e) { if (String(e).includes('must not start with')) { console.error('check interpolated version variable'); } else { throw e; } } Prevention
- Validate version variables in scripts (non-empty, no leading dash)
- Never place CLI flags in version slots
- Use channel names like 'latest' instead of ad-hoc flags
- Quote shell variables to avoid empty-string collapses
When it happens
Trigger: ToolRequest::new_with_options -> validate_version_string receives a non-empty version string whose first character is '-' — e.g. passing 'node@--lts' or a script interpolating a flag into a version slot.
Common situations: Scripts building tool@version strings from variables where the variable is empty (leaving a leading dash), shell arg parsing mistakes, copying CLI flags into version fields in mise.toml.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- brew-cask: invalid {kind} '{value}'
- {option}: '{name}' must be a plain file name (no path separa
- {option}: '{path}' must be a safe relative path (no absolute
- invalid relay path encoding
- invalid relay path
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/f3e6a7dd3c85716a.
Report an issue: GitHub.