jdx/mise · error

invalid tool ref : contains forbidden character

Error message

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

What it means

Ref strings are held to the same character rules as version strings: validate_ref_string rejects any ref containing a character disallowed by is_forbidden_version_char, naming the offending character. This keeps git ref arguments and derived paths safe.

Solutions

  1. Rename/choose a ref without forbidden characters (the message names the culprit).
  2. Trim and sanitize templated config values before writing mise.toml.
  3. Reference the commit SHA instead of an oddly named ref when renaming is impossible.

Example fix

// before
node = { version = "ref:feature/foo bar" }
// after
node = { version = "ref:feature/foo-bar" }  # or a rev: SHA
Defensive patterns

Strategy: validation

Validate before calling

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

Prevention

When it happens

Trigger: A ref:/branch:/tag:/rev: value containing shell metacharacters or whitespace (e.g. ref:"my branch", rev:$(cmd)) reaching new_with_options.

Common situations: Branch names with spaces or special characters pasted unquoted; command substitution accidentally left in config; trailing whitespace/newlines from editor or templating.

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/3a643f48f708d9bf. Report an issue: GitHub.

Appendix: source

Thrown at src/toolset/tool_request.rs:745

}

/// 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.
///
/// The list is written for a POSIX shell, which is why `\` is on it. On Windows `\` is a path
/// separator instead, so it is rewritten by [`windows_path_separators`] before it gets here rather
/// than being allowed through — see that function. The shell those hooks run through there is
/// `cmd.exe`, whose metacharacters are a different set, so a few more are rejected on Windows —
/// see [`is_forbidden_path_char`].

View on GitHub (pinned to 533346cc37)