jdx/mise · error

cannot write task stub because

Error message

cannot write task stub because {} is a symbolic link

What it means

mise refuses to write a task stub if the target path is currently a symbolic link. Stub files are generated artifacts that mise manages directly; writing through an arbitrary symlink could modify an unintended file elsewhere, so it fails closed instead.

Solutions

  1. Remove the symlink at the stub path so mise can write a real file
  2. Replace the symlink with an actual copy of the target script if it must live in the tasks directory
  3. Keep the symlink but exclude it from stub generation by renaming/relocating the task

Example fix

// before
.mise/tasks/build -> ~/scripts/build.sh
// after
rm .mise/tasks/build && cp ~/scripts/build.sh .mise/tasks/build && mise generate task-stubs
Defensive patterns

Strategy: validation

Validate before calling

const st = fs.lstatSync(stubPath, { throwIfNoEntry: false });
if (st?.isSymbolicLink()) {
  throw new Error(`${stubPath} is a symlink; mise generate task-stubs will refuse it`);
}

Type guard

const isSymlink = (p: string): boolean => {
  const st = fs.lstatSync(p, { throwIfNoEntry: false });
  return !!st?.isSymbolicLink();
};

Try / catch

try {
  execSync('mise generate task-stubs');
} catch (e) {
  if (String(e).includes('is a symbolic link')) {
    console.error('Replace the symlink with a real copy or remove it, then rerun');
  }
}

Prevention

When it happens

Trigger: Running `mise generate task-stubs` where an expected stub path (e.g. .mise/tasks/foo) is a symlink — often left by a user linking to a script elsewhere or by an earlier manual setup.

Common situations: Developers symlinked task scripts into the tasks directory; dotfile managers (stow, chezmoi) replaced task files with symlinks; old setups predating stub generation.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

                migrations.insert(StubMigration::File(stub.legacy_path.clone()));
            }
            Ok(metadata) if metadata.file_type().is_dir() => {}
            Ok(_) => bail!(
                "cannot create nested task stubs because {} is not a directory",
                display_path(&stub.legacy_path)
            ),
            Err(err) if matches!(err.kind(), ErrorKind::NotFound | ErrorKind::NotADirectory) => {}
            Err(err) => return Err(err.into()),
        }
    }

    for stub in stubs {
        match fs::symlink_metadata(&stub.path) {
            Ok(metadata) if metadata.file_type().is_dir() => {
                validate_generated_stub_directory(&stub.path, &stub.output, stub.task, launchers)?;
                migrations.insert(StubMigration::Directory(stub.path.clone()));
            }
            Ok(metadata) if metadata.file_type().is_symlink() => bail!(
                "cannot write task stub because {} is a symbolic link",
                display_path(&stub.path)
            ),
            Ok(metadata) if metadata.file_type().is_file() => {
                let existing = file::read_to_string(&stub.path)?;
                let legacy_leaf = stub.legacy_path == stub.path && existing == stub.legacy_output;
                if existing != stub.output && !legacy_leaf {
                    bail!(
                        "cannot write task stub because {} is not a generated task stub",
                        display_path(&stub.path)
                    );
                }
            }
            Ok(_) => bail!(
                "cannot write task stub because {} is not a regular file",
                display_path(&stub.path)
            ),
            Err(err) if matches!(err.kind(), ErrorKind::NotFound | ErrorKind::NotADirectory) => {}

View on GitHub (pinned to 533346cc37)