{"record":{"id":"f3e6a7dd3c85716a","repo":"jdx/mise","slug":"invalid-tool-version-s-must-not-start-with","errorCode":null,"errorMessage":"invalid tool version {s:?}: must not start with '-'","messagePattern":"invalid tool version (.+?): must not start with '-'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/toolset/tool_request.rs","lineNumber":628,"sourceCode":"/// install path names and (for vfox plugins) into `ctx.version` / `ctx.rootPath`\n/// values that downstream Lua hooks often interpolate into shell commands.\n///\n/// The deny list is the minimum set of characters that can break out of either\n/// a single- or double-quoted shell string, or that trigger expansion *inside*\n/// double quotes: quotes themselves, backslash, backtick, and `$`. Plus control\n/// characters (newlines split shell tokens) and `..` (filesystem traversal).\n/// Everything else is allowed so legitimate version vocabulary (npm-style\n/// semver ranges like `>=20 <21 || >=22` or `^1.0.0`, dates, channel names,\n/// `lts/hydrogen`, etc.) continues to work — those characters are only\n/// dangerous in *unquoted* shell context, which cannot occur without one of\n/// the rejected expansion characters appearing first. Leading dashes are also\n/// rejected so backend install tools cannot mistake a version for a CLI flag.\nfn validate_version_string(s: &str) -> Result<()> {\n    if s.is_empty() {\n        return Ok(());\n    }\n    if s.starts_with('-') {\n        bail!(\"invalid tool version {s:?}: must not start with '-'\");\n    }\n    if s.contains(\"..\") {\n        bail!(\"invalid tool version {s:?}: contains path-traversal sequence\");\n    }\n    if let Some(c) = s.chars().find(|c| is_forbidden_version_char(*c)) {\n        bail!(\"invalid tool version {s:?}: contains forbidden character {c:?}\");\n    }\n    Ok(())\n}\n\n/// Validate `ref:`/`branch:`/`tag:`/`rev:` values. Same character rules as\n/// version strings: branch/tag names already use the same broad vocabulary\n/// (`/`, `+`, `-`, etc.), so only shell-quote-breaking characters and leading\n/// dashes need rejection. Kept as a separate function for distinct error\n/// messages.\nfn validate_ref_string(s: &str) -> Result<()> {\n    if s.is_empty() {\n        return Ok(());","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/toolset/tool_request.rs#L610-L646","documentation":"validate_version_string rejects version strings that could be mistaken for CLI flags. A version starting with '-' would be parsed as an option by backend install tools (cargo, go, etc.), so mise rejects it up front with this error.","triggerScenarios":"ToolRequest::new_with_options -> validate_version_string receives a non-empty version string whose first character is '-' — e.g. passing 'node@--lts' or a script interpolating a flag into a version slot.","commonSituations":"Scripts building tool@version strings from variables where the variable is empty (leaving a leading dash), shell arg parsing mistakes, copying CLI flags into version fields in mise.toml.","solutions":["Remove the leading '-' from the version string","Fix the script/variable interpolation producing the empty or flag-like version","Use a concrete version or valid channel like 'latest'","Quote and validate user input before embedding it in tool@version strings"],"exampleFix":"// before\nnode = '--latest'\n// after\nnode = 'latest'","handlingStrategy":"validation","validationCode":"// reject flag-like versions before calling mise\nif (typeof version === 'string' && version.startsWith('-')) throw new Error('version must not start with -');","typeGuard":"const safeVersion = (v) => typeof v === 'string' && v.length > 0 && !v.startsWith('-') && !v.includes('..');","tryCatchPattern":"try { installTool(name, version) } catch (e) { if (String(e).includes('must not start with')) { console.error('check interpolated version variable'); } else { throw e; } }","preventionTips":["Validate version variables in scripts (non-empty, no leading dash)","Never place CLI flags in version slots","Use channel names like 'latest' instead of ad-hoc flags","Quote shell variables to avoid empty-string collapses"],"tags":["validation","version","security"],"backgroundTag":"invalid-argument-value","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}