jdx/mise · error
invalid tool path {s:?}: contains forbidden character {c:?}
Error message
invalid tool path {s:?}: contains forbidden character {c:?} What it means
validate_path_string rejects `path:` tool values containing characters forbidden for paths (after the Windows backslash rewrite, which is reported separately as error 1494). The message names the first offending character, caught during ToolRequest construction via new_with_options.
Source
Thrown at src/toolset/tool_request.rs:691
/// 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 afd2eddd3a)
Solutions
- Remove/rename the quoted forbidden character in the path (the error names it).
- Ensure proper TOML quoting: wrap paths with spaces in double quotes and escape backslashes (`"C:\\tools"`) or use forward slashes (`path:C:/tools`).
- Trim whitespace and retype smart quotes as plain ASCII in the config.
Example fix
// before (mise.toml) [tools] "path:C:/my 'tools'" = "1" // after [tools] "path:C:/my-tools" = "1"
Defensive patterns
Strategy: validation
Validate before calling
function isValidToolPath(p) {
return typeof p === 'string' && p.length > 0 && !/["';`$&|<>(){}\n\r]/.test(p);
}
if (!isValidToolPath(p)) throw new Error(`bad path value: ${p}`); Type guard
function isSafePath(v) { return typeof v === 'string' && v === v.trim() && !/[^\w :\\/().+-]/.test(v); } Prevention
- Use correct TOML string quoting for paths with spaces or backslashes.
- Prefer forward slashes in mise.toml paths to dodge escaping issues.
- Retype paths manually when copy-pasting to avoid smart quotes and control characters.
When it happens
Trigger: A `path:` value containing quotes, semicolons, control characters, or other disallowed symbols, e.g. `path:"C:/my tools;rm"` or a config value with a stray quote or newline.
Common situations: Quoting mistakes in mise.toml (unbalanced quotes end up inside the path), shell interpolation leaking separators, or copy-pasted paths containing smart quotes or trailing whitespace/control chars.
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
- oci mount_point must be an absolute path (got {mount_point:?
- path "{path_raw}" must be absolute or start with ~/, ignorin
- brew-cask: command_wrapper requires a command name without p
- remote mise path does not name an executable: {command:?}
- relative remote mise path does not name an executable: {comm
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/0a2ff7ab0b2d136b.
Report an issue: GitHub.