jdx/mise · error

{tool}'s bin/install exited successfully but installed nothi

Error message

{tool}'s bin/install exited successfully but installed nothing into {}; check that the plugin installs into $ASDF_INSTALL_PATH and that it fails when the build fails

What it means

After running an asdf plugin's bin/install script, mise verifies the install path is non-empty. If the script exits 0 but leaves $ASDF_INSTALL_PATH empty, the install is treated as a silent failure — asdf plugins are expected to fail non-zero when their build fails — so mise raises this error naming the tool and the (display-path-formatted) install directory.

Source

Thrown at src/backend/asdf.rs:549

/// Verifies that `bin/install` actually put something in `$ASDF_INSTALL_PATH`.
///
/// `bin/install` is a plugin-supplied shell script, and one that installs nothing can still exit 0
/// — a missing `set -e`, a build that fails inside a pipeline, a download that 404s. mise would
/// otherwise report `✓ installed`, record the version in install state, and keep resolving to an
/// empty directory afterwards (#5288).
///
/// Emptiness is exact rather than a guess: `Backend::create_install_dirs` recreates the install
/// path immediately before the script runs, and the `incomplete` marker lives under the cache dir,
/// so anything present afterwards came from the plugin. It is also all that can be checked — asdf
/// plugins install into `bin/`, `libexec/`, an unpacked tarball root, or wherever the tool puts
/// things, so a stricter test (spm requires an executable in `bin/`) would reject working plugins.
///
/// There is no `system` case to exclude: anything reaching here has already been through
/// `script_man_for_tv`, which panics for `ToolRequest::System`.
fn verify_install_script_output(tool: &str, install_path: &Path) -> Result<()> {
    if file::ls(install_path)?.is_empty() {
        bail!(
            "{tool}'s bin/install exited successfully but installed nothing into {}; check that the plugin installs into $ASDF_INSTALL_PATH and that it fails when the build fails",
            file::display_path(install_path)
        );
    }
    Ok(())
}

impl Debug for AsdfBackend {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("AsdfPlugin")
            .field("name", &self.name)
            .field("plugin_path", &self.plugin_path)
            .field("cache_path", &self.ba.cache_path)
            .field("downloads_path", &self.ba.downloads_path)
            .field("installs_path", &self.ba.installs_path)
            .field("repo_url", &self.repo_url)
            .finish()
    }

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Inspect/fix the plugin's bin/install so it writes into $ASDF_INSTALL_PATH and exits non-zero on failure (add `set -euo pipefail`).
  2. Re-run the install and check the plugin's logs for a masked download/build failure.
  3. Update or reinstall the plugin (`mise plugin update <tool>` or remove/re-add) to get a working version.
  4. Use a core/backend implementation of the tool instead of the asdf plugin if available.

Example fix

// before (bin/install)
curl -fsSL $download_url -o $ASDF_INSTALL_PATH/bin/tool || true

// after (bin/install)
set -euo pipefail
mkdir -p "$ASDF_INSTALL_PATH/bin"
curl --fail -fsSL "$download_url" -o "$ASDF_INSTALL_PATH/bin/tool"
chmod +x "$ASDF_INSTALL_PATH/bin/tool"
Defensive patterns

Strategy: fallback

Validate before calling

# sanity-check plugin install output location before relying on it
mise plugin list --urls
ls "$(mise where "$TOOL" 2>/dev/null)" 2>/dev/null || echo 'nothing installed'

Try / catch

if ! mise install "$TOOL@$VER"; then
  echo "asdf plugin produced empty install; updating plugin"
  mise plugin update "$TOOL" && mise install "$TOOL@$VER"
fi

Prevention

When it happens

Trigger: `mise install <asdf-plugin-tool>@<version>` where the plugin's bin/install exits successfully without writing anything into $ASDF_INSTALL_PATH; called from install_version_ via verify_install_script_output.

Common situations: Broken or stale asdf plugin whose download failed but script masked the error; plugin written for a different layout (installs to a wrong directory); plugin bug where a build step silently no-ops; plugin lacks strict `set -e` behavior so failures are swallowed.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/68715511d7b2bd4f. Report an issue: GitHub.