jdx/mise · error · eyre::Report

target is not the managed directory, use --force to remove i

Error message

target is not the managed directory, use --force to remove it

What it means

For `symlink-each` entries (mise recreates the source directory's structure under the target and symlinks each file individually), the target must be a real directory that mise seeded. If the target itself is a symlink or an existing non-directory file, mise cannot enumerate owned children and refuses removal without `--force`.

Source

Thrown at src/system/files.rs:1556

                        "target symlink points to {}, use --force to remove it",
                        dest.display_user()
                    );
                }
            } else if req.target.exists() {
                if opts.force {
                    paths.insert(req.target.clone(), ());
                } else {
                    bail!("target is not the managed symlink, use --force to remove it");
                }
            }
        }
        FileMode::SymlinkEach if !cfg!(windows) => {
            clear_symlink_each_state = symlink_each_state_path(req).exists();
            if req.target.is_symlink() || (req.target.exists() && !req.target.is_dir()) {
                if opts.force {
                    paths.insert(req.target.clone(), ());
                } else {
                    bail!("target is not the managed directory, use --force to remove it");
                }
            } else if req.target.is_dir() {
                for path in owned_links(req)? {
                    paths.insert(path, ());
                }
                cleanup_empty_dirs = true;
            }
        }
        FileMode::Copy | FileMode::SymlinkEach if req.source.is_dir() => {
            if req.target.is_symlink() || (req.target.exists() && !req.target.is_dir()) {
                if opts.force {
                    paths.insert(req.target.clone(), ());
                } else {
                    bail!("target is not the managed directory, use --force to remove it");
                }
            } else if req.target.is_dir() {
                for (source, target) in walk_source_files(req)? {
                    plan_regular_file(&source, &target, opts.force, &mut paths)?;

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Restore the target to a plain directory (remove the replacement, `mkdir`), re-apply, then unapply
  2. Use `--force` if the replacement symlink/file is disposable
  3. Fix the entry's mode/source in mise.toml if it was misdeclared for this target

Example fix

# before: ~/.config/ghostty was replaced by a symlink
$ ls -ld ~/.config/ghostty
lrwxrwxrwx 1 me me 22 ~/.config/ghostty -> /home/me/dotfiles/ghostty
$ mise bootstrap dotfiles unapply
Error: target is not the managed directory, use --force to remove it

# after: keep the checkout elsewhere, restore a real dir, re-apply, unapply
$ rm ~/.config/ghostty && mkdir ~/.config/ghostty
$ mise bootstrap dotfiles apply && mise bootstrap dotfiles unapply
Defensive patterns

Strategy: validation

Validate before calling

# symlink-each targets must be plain directories.
[ -L ~/.config/ghostty ] && { echo 'target is a symlink'; exit 1; }
[ -e ~/.config/ghostty ] && [ ! -d ~/.config/ghostty ] && { echo 'target not a dir'; exit 1; }

Type guard

fn is_not_managed_dir(err: &miette::Report) -> bool {
    err.to_string()
        .starts_with("target is not the managed directory")
}

Prevention

When it happens

Trigger: `unapply` of a `mode = "symlink-each"` entry (non-Windows) where `req.target.is_symlink()` or the target exists and is not a directory — e.g. ~/.config/foo was replaced by a symlink to a dotfiles checkout, or turned into a file.

Common situations: User replaced the target directory with a symlink to save space; the directory was converted to a file; entry mode changed from symlink to symlink-each without re-apply.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/27a113f1e53e0415. Report an issue: GitHub.