jdx/mise · error
invalid tool path : contains forbidden character
Error message
invalid tool path {s:?}: contains forbidden character {c:?} What it means
path: tool values have their own permissive but nonzero character allowlist (is_forbidden_path_char), since paths legitimately contain '/', spaces, etc. Any other forbidden character is rejected with a message naming the character, after Windows-specific backslash handling.
Solutions
- Remove/replace the flagged character (it is named in the message).
- Expand variables before writing config, or use mise templating/env correctly.
- On Unix, convert Windows separators: C:\tools\node -> C:/tools/node (or a POSIX path).
Example fix
// before node = "path:$HOME/tools/'my node'" // after node = "path:~/tools/my-node" # expand and avoid quote characters
Defensive patterns
Strategy: validation
Validate before calling
function validPath(p) {
return typeof p === 'string' && p.length > 0 && !/[\'";$|&`<>]/.test(p);
} Prevention
- Expand $VAR and ~ before writing path: values into config.
- Convert Windows backslash paths on Unix hosts.
- Re-type suspicious paths instead of pasting to avoid invisible characters.
When it happens
Trigger: A path: value containing a character rejected by is_forbidden_path_char (e.g. quotes, ';', '|', control characters, or a stray '\' on non-Windows) passed through new_with_options.
Common situations: Shell interpolation injecting quotes/metacharacters into the path; path with unexpanded $VAR or ~ left literal in config; invisible characters from copy-paste; Windows-style backslash paths on Linux/macOS.
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.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- invalid tool path : extended-length and device paths (\\?\…
- invalid tool ref : contains forbidden character
- invalid tool version
- action prediction payload is too large
- bootstrap_command returned an unsafe mise path
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/0a2ff7ab0b2d136b.
Report an issue: GitHub.
Appendix: source
Thrown at src/toolset/tool_request.rs:781
/// see [`is_forbidden_path_char`].
fn validate_path_string(s: &str) -> Result<()> {
if s.is_empty() {
return Ok(());
}
if let Some(c) = s.chars().find(|c| {
// Allow newlines/tabs/etc. in paths is still bad — keep control-char
// and quote/expansion rejection, but allow `/` since paths need it.
is_forbidden_path_char(*c)
}) {
// The only `\` that survives the rewrite is an extended-length or device prefix, so say
// what is actually wrong instead of naming a character the user cannot avoid.
#[cfg(windows)]
if c == '\\' {
bail!(
"invalid tool path {s:?}: extended-length and device paths (\\\\?\\, \\\\.\\) are not supported"
);
}
bail!("invalid tool path {s:?}: contains forbidden character {c:?}");
}
Ok(())
}
/// Rewrite `\` to `/` in a `path:` value on Windows.
///
/// `\` is the path separator there, not a shell metacharacter, so [`validate_path_string`] used to
/// reject every native path — anything copied out of Explorer or printed by `pwd`. Win32 accepts
/// `/` wherever it accepts `\`, so rewriting is what makes those usable *without* letting a `\`
/// reach a vfox hook's `ctx.rootPath`, which is the thing the list exists to prevent (#9814). The
/// alternative — dropping `\` from the list on Windows — would have weakened that.
///
/// Extended-length and device prefixes (`\\?\`, `\\.\`) are left alone. Those are the one place
/// Windows does not accept `/`, so rewriting would hand back a path that looks right and does not
/// resolve; they keep being rejected, as they are today.
#[cfg(windows)]
fn windows_path_separators(s: &str) -> std::borrow::Cow<'_, str> {
if s.starts_with(r"\\?\") || s.starts_with(r"\\.\") {View on GitHub (pinned to 533346cc37)