{"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":649,"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":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/toolset/tool_request.rs#L631-L667","documentation":"validate_ref_string validates `ref:`/`branch:`/`tag:`/`rev:` values with the same rules as version strings; this arm rejects a ref that begins with '-'. Starting with '-' could be misinterpreted as a CLI flag downstream, so mise refuses it during ToolRequest construction.","triggerScenarios":"A ToolRequest like `git:repo@ref:-weird-branch`, `node@ref:-x`, or a config entry `branch = \"-feature\"` passed through new_with_options.","commonSituations":"Branch or tag names that accidentally begin with a dash (often from scripting, e.g. `branch = \"$(git symbolic-ref --short HEAD)\"` returning an odd value), or typos where the prefix separator got attached to the name.","solutions":["Rename or re-specify the branch/tag without a leading '-', e.g. `ref:feature-x` instead of `ref:-feature-x`.","If the target genuinely has a leading dash, use `rev:<full-sha>` to pin the commit instead of a name.","Fix the script/config that generated the ref string and verify with `mise use node@ref:mybranch`."],"exampleFix":"// before (mise.toml)\n[tools]\n\"github:owner/repo\" = { ref = \"-dev\" }\n// after\n[tools]\n\"github:owner/repo\" = { ref = \"dev\" }","handlingStrategy":"validation","validationCode":"function isValidRef(ref) {\n  return typeof ref === 'string' && (ref === '' || (!ref.startsWith('-') && !ref.includes('..')));\n}\nif (!isValidRef(ref)) throw new Error(`bad ref: ${ref}`);","typeGuard":"function isSafeRef(v) { return typeof v === 'string' && !v.startsWith('-'); }","tryCatchPattern":null,"preventionTips":["Never let generated branch/tag names begin with '-' — prefix them if needed.","Trim and validate output of `git symbolic-ref`/`git describe` before embedding into config.","Prefer `rev:<sha>` for anything derived from automation."],"tags":["validation","cli","git-ref","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"}