{"record":{"id":"dd3b3ff74e1221ba","repo":"jdx/mise","slug":"invalid-tool-ref-s-must-not-start-with","errorCode":null,"errorMessage":"invalid tool ref {s:?}: must not start with '-'","messagePattern":"invalid tool ref (.+?): must not start with '-'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/toolset/tool_request.rs","lineNumber":739,"sourceCode":"        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(());\n    }\n    if s.starts_with('-') {\n        bail!(\"invalid tool ref {s:?}: must not start with '-'\");\n    }\n    if s.contains(\"..\") {\n        bail!(\"invalid tool ref {s:?}: contains path-traversal sequence\");\n    }\n    if let Some(c) = s.chars().find(|c| is_forbidden_version_char(*c)) {\n        bail!(\"invalid tool ref {s:?}: contains forbidden character {c:?}\");\n    }\n    Ok(())\n}\n\n/// Validate `path:` values. Filesystem paths legitimately contain `/`, spaces,\n/// and many other characters, but the resolved path becomes `ctx.rootPath` /\n/// `installPath` for path-mode tools and is interpolated into shell commands\n/// by some plugin hooks. Reject the same shell-quote-breaking characters as\n/// version strings — `$`, backtick, quotes, and `\\` — so a hostile `path:`\n/// entry in a project config cannot inject shell syntax. Path traversal is\n/// intentionally not rejected here because `path:../tools/foo` is a normal\n/// relative-path use case.","sourceCodeStart":721,"sourceCodeEnd":757,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/toolset/tool_request.rs#L721-L757","documentation":"Values given via ref:/branch:/tag:/rev: prefixes are validated with validate_ref_string. A ref starting with '-' is rejected so git/backend invocations cannot mistake the ref for a command-line flag. Empty refs are permitted; dash-prefixed ones are not.","triggerScenarios":"Calling new_with_options with a ref value like ref:-b branch, or config entries such as `tool = { version = 'ref:-x' }` where the ref begins with '-'.","commonSituations":"Copy-pasting git command fragments (-b main) into a ref value; scripts interpolating flags into the ref position; typos in mise.toml tool entries.","solutions":["Remove the leading dash and give a plain ref name (ref:main, tag:v1.2).","If the dash is part of a git option you want, restructure: mise does not accept git flags in ref values.","Check scripts that build ref strings from variables for accidental flag leakage."],"exampleFix":"// mise.toml before\nnode = { version = \"ref:-b main\" }\n// after\nnode = { version = \"ref:main\" }","handlingStrategy":"validation","validationCode":"function validRef(r) {\n  return typeof r === 'string' && (r === '' || !r.startsWith('-'));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Store plain ref names (branch/tag/SHA), never git flags.","Review generated mise.toml for leading dashes in ref fields.","Copy refs from `git branch --format=%(refname:short)` not from command lines."],"tags":["mise","validation","git-ref","cli"],"backgroundTag":"invalid-argument-value","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}