jdx/mise · error

invalid tool version

Error message

invalid tool version {s:?}: contains forbidden character {c:?}

What it means

mise enforces an allowlist of characters in version strings via is_forbidden_version_char. Any version containing a forbidden character (characters that could break shell commands, paths, or backend argument construction) is rejected with this message naming the offending character.

Solutions

  1. Remove or replace the flagged character with an allowed one (check the reported character in the message).
  2. Quote variables in shell scripts and trim input: mise install "foo@${VER// /}".
  3. If the value is not a version, use the correct prefix (ref:, tag:, path:), which has its own validation.

Example fix

// before (script)
mise install node@$VERSION   # VERSION="20.x beta"
// after
VERSION="20.11.0"; mise install "node@$VERSION"
Defensive patterns

Strategy: validation

Validate before calling

function validVersion(v) {
  return typeof v === 'string' && !v.startsWith('-') && !v.includes('..') && !/[\s'";$|&`<>]/.test(v);
}

Prevention

When it happens

Trigger: ToolRequest::new_with_options receiving a version containing a character rejected by is_forbidden_version_char — e.g. spaces, quotes, '$', ';', '|', '&', backticks, newlines.

Common situations: Unquoted shell interpolation carrying whitespace or shell metacharacters into `mise install foo@...`; YAML/TOML values with trailing spaces; pasted versions with invisible characters or trailing newline.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at src/toolset/tool_request.rs:724

/// 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 '-'");
    }
    if s.contains("..") {
        bail!("invalid tool ref {s:?}: contains path-traversal sequence");

View on GitHub (pinned to 533346cc37)