{"record":{"id":"bd989b5647f7089e","repo":"jdx/mise","slug":"cannot-write-windows-launcher-because-is-not-a","errorCode":null,"errorMessage":"cannot write Windows launcher because {} is not a generated launcher","messagePattern":"cannot write Windows launcher because (.+?) is not a generated launcher","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cli/generate/task_stubs.rs","lineNumber":278,"sourceCode":"        }\n    }\n    Ok(migrations.into_iter().collect())\n}\n\n/// Refuse to replace a `.cmd` beside a stub that mise did not write.\n///\n/// The stub path is the user's choice, so `bin/<task>.cmd` is a name a project may already be\n/// using for a script of its own — and unlike the stub itself, nothing about the name says mise\n/// owns it. Checked during validation rather than at the write, so a launcher that is not ours\n/// stops the whole run instead of leaving a half-generated `bin/`.\nfn validate_launcher_path(stub: &TaskStub<'_>) -> Result<()> {\n    let Some(launcher) = super::windows_launcher_path(&stub.path) else {\n        return Ok(());\n    };\n    match fs::symlink_metadata(&launcher) {\n        Ok(metadata) if metadata.file_type().is_file() => {\n            if !super::is_generated_launcher(&file::read_to_string(&launcher)?) {\n                bail!(\n                    \"cannot write Windows launcher because {} is not a generated launcher\",\n                    display_path(&launcher)\n                );\n            }\n        }\n        Ok(_) => bail!(\n            \"cannot write Windows launcher because {} is not a regular file\",\n            display_path(&launcher)\n        ),\n        Err(err) if matches!(err.kind(), ErrorKind::NotFound | ErrorKind::NotADirectory) => {}\n        Err(err) => return Err(err.into()),\n    }\n    Ok(())\n}\n\nfn validate_generated_stub_directory(path: &Path, expected: &str, task: &Task) -> Result<()> {\n    let default = path.join(\"_default\");\n    match fs::symlink_metadata(&default) {","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/cli/generate/task_stubs.rs#L260-L296","documentation":"Next to every stub it writes, mise also writes a Windows launcher `<stub>.cmd` (on every OS, since stubs are committed for Windows contributors). That launcher path already holds a regular file that is not a mise-generated launcher (which must be exactly `@echo off`, `rem generated by mise`, one command line ending in ` %*`). Because bin/<task>.cmd is a name a project may already use for its own batch script, mise refuses to clobber it and stops the whole run during validation, before writing anything.","triggerScenarios":"`mise generate task-stubs` on any OS when bin/<task>.cmd is a hand-written batch file; a previously generated launcher was edited until it no longer matched the generated shape.","commonSituations":"Windows-friendly repo that already ships bin/<name>.cmd wrappers; adopting task-stubs where cmd scripts exist for the same task names.","solutions":["If the .cmd is the project's own, rename it (`git mv bin/task.cmd bin/task-run.cmd`) or port its logic into the task definition","If it is a stale/disposable launcher, delete it: `git rm bin/<task>.cmd`","Pick different task names (or --dir) so the launcher paths do not collide"],"exampleFix":"# before: bin/build.cmd is a hand-written wrapper\n$ mise generate task-stubs\n# error: cannot write Windows launcher because bin/build.cmd is not a generated launcher\n\n# after\n$ git mv bin/build.cmd scripts/build.cmd\n$ mise generate task-stubs","handlingStrategy":"type-guard","validationCode":"#!/bin/bash\n# any existing .cmd next to a stub target must be mise-generated\nfor f in bin/**/*.cmd bin/*.cmd; do\n  [ -f \"$f\" ] || continue\n  is_mise_launcher \"$f\" || { echo \"$f is not a generated launcher\" >&2; exit 1; }\ndone\nmise generate task-stubs","typeGuard":"is_mise_launcher() {\n  [ \"$(head -1 \"$1\")\" = '@echo off' ] && [ \"$(sed -n 2p \"$1\")\" = 'rem generated by mise' ]\n}","tryCatchPattern":"if ! mise generate task-stubs; then\n  echo 'a .cmd beside a stub is hand-written — rename it or fold its logic into the task' >&2\n  exit 1\nfi","preventionTips":["Do not name hand-written batch files <task>.cmd inside the stub directory","Never edit generated .cmd launchers; regeneration will stop recognizing them","Keep Windows wrappers in scripts/ and let mise own bin/<task>.cmd"],"tags":["mise","task-stubs","windows","cmd-launcher","ownership","generate"],"backgroundTag":"generated-file-ownership-conflict","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}