jdx/mise · error
cannot replace task stub directory because
Error message
cannot replace task stub directory because {} does not contain the generated stub for task {} What it means
When `mise generate task-stub` replaces an entire stub directory, it first verifies the `_default` stub inside matches the expected generated content. If the directory has no readable `_default` file whose content equals the generated default stub, mise refuses to replace the directory and throws this error naming the task. This guards against wiping directories that contain user-owned content.
Solutions
- Regenerate stubs into a clean directory instead of replacing the existing one
- Restore the unmodified generated `_default` file (recreate via mise) before replacing the directory
- Back up and remove the directory manually, then re-run `mise generate task-stub`
Example fix
// before: hand-edited _default diverges from generated content // stubs\_default edited by hand -> replacement blocked // after: regenerate from scratch rm -r stubs mise generate task-stub --task build --output stubs
Defensive patterns
Strategy: validation
Validate before calling
const def = path.join(stubDir, '_default');
if (!fs.existsSync(def) || fs.lstatSync(def).isSymbolicLink()) {
throw new Error('stub directory lacks a plain generated _default; regenerate into a clean dir');
} Type guard
const hasPlainDefault = (dir: string): boolean => {
try { return fs.lstatSync(path.join(dir, '_default')).isFile(); } catch { return false; }
}; Try / catch
try {
await exec('mise generate task-stub ...');
} catch (e) {
if (String(e).includes('does not contain the generated stub')) {
fs.rmSync(stubDir, { recursive: true });
await exec('mise generate task-stub ...'); // regenerate fresh
}
} Prevention
- Never hand-edit generated `_default` stubs
- Regenerate stubs after mise version upgrades since templates may change
- Avoid symlink managers over stub directories
When it happens
Trigger: Running `mise generate task-stub` targeting a directory that: lacks a `_default` child, has `_default` as a non-file (symlink/dir), or whose `_default` content differs from the freshly generated default stub text (edited or produced by an older mise version).
Common situations: Users hand-edited `_default`; a stub was generated by an older mise with different template text; a symlink farm (e.g. dotfiles manager) replaced `_default` with a symlink.
Related errors
- cannot replace task stub directory because
- cannot replace task stub directory because
- a repository preview directory requires a dry run
- `--all` needs `--to
- at least one target or --changed is required
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/a385fac14f773f5a.
Report an issue: GitHub.
Appendix: source
Thrown at src/cli/generate/task_stubs.rs:616
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)
if metadata.file_type().is_file() && file::read_to_string(&default)? == expected => {}
_ => bail!(
"cannot replace task stub directory because {} does not contain the generated stub for task {}",
display_path(path),
task.display_name
),
}
validate_generated_stub_tree(path, launchers)?;
Ok(())
}
fn validate_generated_stub_tree(path: &Path, launchers: &Launchers) -> Result<usize> {
let mut files = 0;
for entry in fs::read_dir(path)? {
let entry = entry?;
let entry_path = entry.path();
let metadata = fs::symlink_metadata(&entry_path)?;
if metadata.file_type().is_dir() {
let child_files = validate_generated_stub_tree(&entry_path, launchers)?;View on GitHub (pinned to 533346cc37)