jdx/mise · error

invalid tool ref : must not start with

Error message

invalid tool ref {s:?}: must not start with '-'

What it means

Values given via ref:/branch:/tag:/rev: prefixes are validated with validate_ref_string. A ref starting with '-' is rejected so git/backend invocations cannot mistake the ref for a command-line flag. Empty refs are permitted; dash-prefixed ones are not.

Solutions

  1. Remove the leading dash and give a plain ref name (ref:main, tag:v1.2).
  2. If the dash is part of a git option you want, restructure: mise does not accept git flags in ref values.
  3. Check scripts that build ref strings from variables for accidental flag leakage.

Example fix

// mise.toml before
node = { version = "ref:-b main" }
// after
node = { version = "ref:main" }
Defensive patterns

Strategy: validation

Validate before calling

function validRef(r) {
  return typeof r === 'string' && (r === '' || !r.startsWith('-'));
}

Prevention

When it happens

Trigger: Calling new_with_options with a ref value like ref:-b branch, or config entries such as `tool = { version = 'ref:-x' }` where the ref begins with '-'.

Common situations: Copy-pasting git command fragments (-b main) into a ref value; scripts interpolating flags into the ref position; typos in mise.toml tool entries.

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


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

Appendix: source

Thrown at src/toolset/tool_request.rs:739

        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 '-'");
    }
    if s.contains("..") {
        bail!("invalid tool ref {s:?}: contains path-traversal sequence");
    }
    if let Some(c) = s.chars().find(|c| is_forbidden_version_char(*c)) {
        bail!("invalid tool ref {s:?}: contains forbidden character {c:?}");
    }
    Ok(())
}

/// Validate `path:` values. Filesystem paths legitimately contain `/`, spaces,
/// and many other characters, but the resolved path becomes `ctx.rootPath` /
/// `installPath` for path-mode tools and is interpolated into shell commands
/// by some plugin hooks. Reject the same shell-quote-breaking characters as
/// version strings — `$`, backtick, quotes, and `\` — so a hostile `path:`
/// entry in a project config cannot inject shell syntax. Path traversal is
/// intentionally not rejected here because `path:../tools/foo` is a normal
/// relative-path use case.

View on GitHub (pinned to 533346cc37)