{"record":{"id":"68715511d7b2bd4f","repo":"jdx/mise","slug":"tool-s-bin-install-exited-successfully-but-insta","errorCode":null,"errorMessage":"{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","messagePattern":"(.+?)'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","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/backend/asdf.rs","lineNumber":549,"sourceCode":"\n/// Verifies that `bin/install` actually put something in `$ASDF_INSTALL_PATH`.\n///\n/// `bin/install` is a plugin-supplied shell script, and one that installs nothing can still exit 0\n/// — a missing `set -e`, a build that fails inside a pipeline, a download that 404s. mise would\n/// otherwise report `✓ installed`, record the version in install state, and keep resolving to an\n/// empty directory afterwards (#5288).\n///\n/// Emptiness is exact rather than a guess: `Backend::create_install_dirs` recreates the install\n/// path immediately before the script runs, and the `incomplete` marker lives under the cache dir,\n/// so anything present afterwards came from the plugin. It is also all that can be checked — asdf\n/// plugins install into `bin/`, `libexec/`, an unpacked tarball root, or wherever the tool puts\n/// things, so a stricter test (spm requires an executable in `bin/`) would reject working plugins.\n///\n/// There is no `system` case to exclude: anything reaching here has already been through\n/// `script_man_for_tv`, which panics for `ToolRequest::System`.\nfn verify_install_script_output(tool: &str, install_path: &Path) -> Result<()> {\n    if file::ls(install_path)?.is_empty() {\n        bail!(\n            \"{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\",\n            file::display_path(install_path)\n        );\n    }\n    Ok(())\n}\n\nimpl Debug for AsdfBackend {\n    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n        f.debug_struct(\"AsdfPlugin\")\n            .field(\"name\", &self.name)\n            .field(\"plugin_path\", &self.plugin_path)\n            .field(\"cache_path\", &self.ba.cache_path)\n            .field(\"downloads_path\", &self.ba.downloads_path)\n            .field(\"installs_path\", &self.ba.installs_path)\n            .field(\"repo_url\", &self.repo_url)\n            .finish()\n    }","sourceCodeStart":531,"sourceCodeEnd":567,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/backend/asdf.rs#L531-L567","documentation":"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.","triggerScenarios":"`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.","commonSituations":"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.","solutions":["Inspect/fix the plugin's bin/install so it writes into $ASDF_INSTALL_PATH and exits non-zero on failure (add `set -euo pipefail`).","Re-run the install and check the plugin's logs for a masked download/build failure.","Update or reinstall the plugin (`mise plugin update <tool>` or remove/re-add) to get a working version.","Use a core/backend implementation of the tool instead of the asdf plugin if available."],"exampleFix":"// before (bin/install)\ncurl -fsSL $download_url -o $ASDF_INSTALL_PATH/bin/tool || true\n\n// after (bin/install)\nset -euo pipefail\nmkdir -p \"$ASDF_INSTALL_PATH/bin\"\ncurl --fail -fsSL \"$download_url\" -o \"$ASDF_INSTALL_PATH/bin/tool\"\nchmod +x \"$ASDF_INSTALL_PATH/bin/tool\"","handlingStrategy":"fallback","validationCode":"# sanity-check plugin install output location before relying on it\nmise plugin list --urls\nls \"$(mise where \"$TOOL\" 2>/dev/null)\" 2>/dev/null || echo 'nothing installed'","typeGuard":null,"tryCatchPattern":"if ! mise install \"$TOOL@$VER\"; then\n  echo \"asdf plugin produced empty install; updating plugin\"\n  mise plugin update \"$TOOL\" && mise install \"$TOOL@$VER\"\nfi","preventionTips":["Write plugin bin/install scripts with `set -euo pipefail` and explicit non-zero exits on failure.","Always install into $ASDF_INSTALL_PATH, never a hardcoded directory.","Test plugins by checking the install dir is non-empty after a successful run.","Keep plugins updated; stale plugins are the usual cause of silent no-op installs."],"tags":["asdf","plugin","install-verification","silent-failure"],"backgroundTag":"empty-result-set","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}