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
- Remove the symlink at the stub path so mise can write a real file
- Replace the symlink with an actual copy of the target script if it must live in the tasks directory
- 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
- Copy task scripts into the tasks directory instead of symlinking them
- Configure dotfile managers (stow/chezmoi) to skip the tasks directory
- Check for symlinks before committing generated stub layouts
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
- cannot create nested task stubs because
- cannot create nested task stubs because
- cannot create task stub directory because
- cannot link to its own install path
- cannot write native task stub launchers: mise-shim.exe was…
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)