jdx/mise · error

cannot write task stub because {} is a symbolic link

Error message

cannot write task stub because {} is a symbolic link

What it means

The target stub path (bin/<task> or bin/<parent>/_default) is a symbolic link. Writing through a symlink would modify whatever it points at, which mise cannot treat as its own output, so generation stops and names the link.

Source

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

                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)?;
                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 9dcfcaa0dc)

Solutions

  1. Remove the symlink (`git rm bin/<task>` for tracked, plain `rm` otherwise) and rerun `mise generate task-stubs`
  2. If the link is load-bearing, rename the task or change --dir so they no longer collide
  3. Adopt the committed-stub workflow the command exists for: generated stubs plus `mise generate bootstrap` replace symlink farms

Example fix

# before
$ ls -l bin/build   # bin/build -> ../tools/build
$ mise generate task-stubs
# error: bin/build is a symbolic link

# after
$ git rm bin/build
$ mise generate task-stubs
Defensive patterns

Strategy: type-guard

Validate before calling

#!/bin/bash
# refuse to generate over symlinks
find bin -type l -print -quit | grep -q . && { echo 'symlink under bin/' >&2; exit 1; }
mise generate task-stubs

Type guard

is_writable_path() {
  # 0 when path is absent or a real file/dir (not a symlink)
  [ ! -L "$1" ] && { [ ! -e "$1" ] || [ -f "$1" ] || [ -d "$1" ]; }
}

Prevention

When it happens

Trigger: `mise generate task-stubs` when bin/<task> is a symlink to elsewhere — e.g. a repo still using the bin/-full-of-symlinks convention (bin/foo -> ../tools/foo).

Common situations: Migrating a repo from symlink-style bin/ dispatch to committed stubs; contributors with divergent bin/ setups; symlinks created by an installer script.

Related errors


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