{"record":{"id":"be7a6086857d838e","repo":"jdx/mise","slug":"localized-dir-raw-cannot-be-carried-to-windo","errorCode":null,"errorMessage":"--localized-dir {raw:?} cannot be carried to Windows: {bad:?} is not allowed in a path component there, so the generated launcher could not create the directory. Drop --windows, or pick a name Windows accepts.","messagePattern":"--localized-dir (.+?) cannot be carried to Windows: (.+?) is not allowed in a path component there, so the generated launcher could not create the directory\\. Drop --windows, or pick a name Windows accepts\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cli/generate/bootstrap.rs","lineNumber":242,"sourceCode":"    rest.trim_start_matches(['\\\\', '/'])\n}\n\n/// The `localized_dir` value for the batch script — the same two rules the bash branch applies:\n/// escape the value, and join it to the project directory only when it is relative.\n///\n/// Refuses a value Windows could not hold rather than emitting one. A directory named `C:foo` is\n/// ordinary on Linux and the bash half installs into it happily, but on Windows `C:foo` is\n/// drive-*relative*, so `%project_dir%\\C:foo` is not a path at all and `mkdir` fails — on the\n/// contributor's machine, not on the machine that generated the file. Calling it rooted instead\n/// would be worse: that drops `%project_dir%\\` and installs wherever cmd's per-drive working\n/// directory happens to point, silently diverging from the bash half.\nfn windows_localized_dir(dir: &Path) -> Result<String> {\n    let raw = dir.to_string_lossy();\n    if let Some(bad) = windows_path_components(&raw)\n        .chars()\n        .find(|c| WINDOWS_FORBIDDEN_IN_COMPONENT.contains(c))\n    {\n        bail!(\n            \"--localized-dir {raw:?} cannot be carried to Windows: {bad:?} is not allowed in a \\\n             path component there, so the generated launcher could not create the directory. \\\n             Drop --windows, or pick a name Windows accepts.\"\n        );\n    }\n    let escaped = cmd_escape(&raw);\n    match is_windows_absolute(&escaped) {\n        true => Ok(escaped),\n        false => Ok(format!(r\"%project_dir%\\{escaped}\")),\n    }\n}\n\n/// Escape a value for interpolation into a `set \"var=…\"` line in the launcher.\n///\n/// Only `%` needs it. The script keeps delayed expansion off, so `!` is already an ordinary\n/// character there — measured: with it off, `set \"d=…\\p6-od!d\"` followed by `mkdir \"%d%\"` creates\n/// `p6-od!d`, and with it on the same two lines create `p6-odd`. A raw `%` would still be read at\n/// parse time, so with `x` set a directory named `my%x%dir` becomes `myCLOBBEREDdir`.","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/cli/generate/bootstrap.rs#L224-L260","documentation":"With `--localize`, `mise generate bootstrap --windows` embeds --localized-dir into a .cmd launcher that mkdir's it (relative dirs are joined to %project_dir%). Windows forbids < > : \" | ? * inside a path component — and a component like `C:foo` is drive-relative there, not a path — so the generator refuses the value rather than emitting a launcher that would fail later on the contributor's Windows machine.","triggerScenarios":"`mise generate bootstrap --localize --localized-dir <dir> --windows` where any component of <dir> contains one of the seven forbidden characters (a colon is the common trap: names like `C:foo` or `cache:dev` are legal on Linux/macOS but unusable on Windows).","commonSituations":"Unix project localizing into a timestamp/URL-derived name containing colons (`backup:2026`, `https://` fragments); passing a drive-relative Windows path by mistake; script composing the dir from unvalidated input.","solutions":["Pick a --localized-dir whose components avoid < > : \" | ? * (e.g. .mise-local, cache-dev)","Drop `--windows` to generate only the bash bootstrap, which accepts the Unix-legal name","Keep the odd name for the Unix script and generate the Windows launcher separately with a Windows-safe directory"],"exampleFix":"# before\nmise generate bootstrap --localize --localized-dir 'cache:dev' --windows\n# error: ':' is not allowed in a path component there\n\n# after\nmise generate bootstrap --localize --localized-dir 'cache-dev' --windows","handlingStrategy":"validation","validationCode":"#!/bin/bash\n# reject Windows-forbidden chars before generating\nif printf %s \"$LOCALIZED_DIR\" | grep -q '[<>:\"|?*]'; then\n  echo \"--localized-dir '$LOCALIZED_DIR' cannot be carried to Windows\" >&2\n  exit 1\nfi\nmise generate bootstrap --localize --localized-dir \"$LOCALIZED_DIR\" --windows","typeGuard":"windows_safe_dir() {\n  # returns 0 when every component avoids < > : \" | ? *\n  case \"$1\" in\n    *[\\<\\>:\\\"\\|\\?*]*) return 1 ;;\n    *) return 0 ;;\n  esac\n}","tryCatchPattern":null,"preventionTips":["Constrain --localized-dir to [A-Za-z0-9._-] when generating cross-platform bootstraps","Remember a leading drive prefix (C:foo) is drive-relative on Windows — avoid colons entirely","Generate the Windows variant in CI from a fixed, reviewed constant rather than dynamic input"],"tags":["mise","bootstrap","windows","path-validation","generate","localize"],"backgroundTag":"invalid-filename-characters","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}