jdx/mise · error

cannot write Windows launcher because

Error message

cannot write Windows launcher because {} is not a regular file

What it means

This error comes from mise's `mise generate task-stub` path validation on Windows. Before writing the Windows `.cmd`/`.ps1` launcher next to a stub, mise stats the launcher path; if it exists but is not a regular file (e.g. it is a directory or symlink), it refuses to overwrite and throws this error. This protects users from clobbering something mise did not generate itself.

Solutions

  1. Remove or rename the non-regular file (directory/symlink) at the launcher path so mise can write a fresh launcher
  2. Check what the path is with `Get-Item` / `dir` and restore it to a real file or delete it
  3. Re-run `mise generate task-stub` after clearing the path

Example fix

// before: launcher path is a directory
mkdir stubs\mise-task-build.cmd   # blocks launcher creation
// after: remove it so mise can generate
rmdir stubs\mise-task-build.cmd
mise generate task-stub --task build
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'node:fs';
if (fs.existsSync(launcherPath)) {
  const st = fs.lstatSync(launcherPath);
  if (!st.isFile()) throw new Error(`clear non-file at ${launcherPath} before generating stubs`);
}

Type guard

const isRegularFile = (p: string): boolean => { try { return fs.lstatSync(p).isFile(); } catch { return false; } };

Try / catch

try {
  await exec('mise generate task-stub ...');
} catch (e) {
  if (String(e).includes('is not a regular file')) {
    fs.rmSync(launcherPath, { recursive: true }); // after user confirms
  }
}

Prevention

When it happens

Trigger: Running `mise generate task-stub` (Windows) where the computed launcher path for the task already exists as a non-regular file: a directory, a symlink, or a special file at that location. symlink_metadata succeeds, metadata is not file-type regular, so the `Ok(_)` arm bails.

Common situations: A user manually created a directory or shortcut at the launcher path (e.g. `mise-task-foo.cmd` replaced by a folder); a previous tooling run left a symlink there; synced/backup software replaced files with symlinks.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/ee9275c096880e06. Report an issue: GitHub.

Appendix: source

Thrown at src/cli/generate/task_stubs.rs:596

/// The stub path is the user's choice, so `bin/<task>.cmd` is a name a project may already be
/// using for a script of its own — and unlike the stub itself, nothing about the name says mise
/// owns it. `bin/<task>.exe` is the same problem with less to go on, which is why ownership there
/// rests on the bytes. Checked during validation rather than at the write, so a launcher that is
/// not ours stops the whole run instead of leaving a half-generated `bin/`.
fn validate_launcher_path(stub: &TaskStub<'_>, launchers: &Launchers) -> Result<()> {
    let Some(launcher) = launchers.path(&stub.path) else {
        return Ok(());
    };
    match fs::symlink_metadata(&launcher) {
        Ok(metadata) if metadata.file_type().is_file() => {
            if !launchers.owns(&launcher) {
                bail!(
                    "cannot write Windows launcher because {} is not a generated launcher",
                    display_path(&launcher)
                );
            }
        }
        Ok(_) => bail!(
            "cannot write Windows launcher because {} is not a regular file",
            display_path(&launcher)
        ),
        Err(err) if matches!(err.kind(), ErrorKind::NotFound | ErrorKind::NotADirectory) => {}
        Err(err) => return Err(err.into()),
    }
    Ok(())
}

fn validate_generated_stub_directory(
    path: &Path,
    expected: &str,
    task: &Task,
    launchers: &Launchers,
) -> Result<()> {
    let default = path.join("_default");
    match fs::symlink_metadata(&default) {
        Ok(metadata)

View on GitHub (pinned to 533346cc37)