jdx/mise · error

{name} does not support declarative package removal

Error message

{name} does not support declarative package removal

What it means

During `mise system packages install`, the driver computes `remove_targets`: packages whose desired state is `Absent` (declarative removal). If removal targets exist but the manager's `supports_remove()` returns false, the operation cannot honor the declarative spec and mise bails rather than silently leaving packages installed.

Source

Thrown at src/cli/system/driver.rs:187

        let installed = statuses
            .iter()
            .filter(|status| status.request.desired == PackageDesiredState::Present)
            .filter(|status| status.state.is_installed())
            .count();
        if action == Action::Install && installed > 0 {
            info!("{name}: {installed} package(s) already installed");
        }
        let already_absent = statuses
            .iter()
            .filter(|status| status.request.desired == PackageDesiredState::Absent)
            .filter(|status| matches!(status.state, PackageState::Missing))
            .count();
        if action == Action::Install && already_absent > 0 {
            info!("{name}: {already_absent} package(s) already absent");
        }
        if !remove_targets.is_empty() {
            if !mp.manager.supports_remove() {
                bail!("{name} does not support declarative package removal");
            }
            let remove = remove_targets
                .iter()
                .map(|status| status.request.clone())
                .collect::<Vec<_>>();
            let list = remove
                .iter()
                .map(|request| request.to_string())
                .collect::<Vec<_>>();
            if !d.dry_run && !d.yes && console::user_attended_stderr() {
                let msg = format!("{name}: remove {}?", list.join(", "));
                if !prompt::confirm(msg)?.is_yes() {
                    info!("{name}: removal skipped");
                } else {
                    mp.manager.remove(&remove, &opts).await?;
                    info!("{name}: removed {}", list.join(", "));
                }
            } else {

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Implement `hooks/package_uninstall.lua` (or the remove hook) in the plugin so it supports removal.
  2. Remove the absent-marked package entries from the config, or uninstall those packages manually.
  3. Use a different manager that supports removal for those packages.
  4. Run with --dry-run to see which targets would be removed before adjusting config.

Example fix

// before (mise.toml)
[system_packages]
"myplugin:legacy-tool" = false
// after: remove the entry and uninstall manually, or switch manager
[system_packages]
"brew:legacy-tool" = false
Defensive patterns

Strategy: validation

Validate before calling

# Only mark packages absent for managers that support removal
# confirm with a dry run listing remove targets
mise system packages --dry-run

Prevention

When it happens

Trigger: A system_packages config (or tracked config files) marks a package as absent (`manager:pkg = false` / absent desired state) while the resolved manager plugin lacks a package-remove hook (`supports_remove() == false`).

Common situations: Writing `"plugin:foo" = false` for a minimal vfox plugin that only implements install hooks; sharing a config across machines where the plugin variant differs; expecting `system packages install` to uninstall for a manager that only supports install.

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@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/31dd225946f14e20. Report an issue: GitHub.