asdf-vm/asdf · error

failed to run download callback

Error message

failed to run download callback: %w

What it means

asdf's InstallOneVersion runs the plugin's optional 'download' callback in a temporary download directory. The download callback exists but returned a non-zero exit or the spawned command failed, and the failure was NOT a plugins.NoCallbackError (which is intentionally tolerated since the callback is optional). asdf wraps the underlying error so the plugin author's failure surfaces during installation testing.

Solutions

  1. Inspect the wrapped error and the plugin's download callback script; run it manually to reproduce the failure.
  2. Fix the plugin's download callback (missing dependency, bad URL, wrong shebang, non-zero exit).
  3. Ensure required tools/env vars the callback relies on are available in the install environment.
  4. If the callback should be optional/absent, remove it entirely so NoCallbackError applies and is skipped.

Example fix

// plugin lib/utils.bash download callback failing
# before
curl -sSL "$release_url" | tar xz
# after (check failure and give a clear message)
curl --fail -sSL "$release_url" -o /tmp/pkg.tar.gz || exit 1
mkdir -p "$ASDF_DOWNLOAD_PATH"
tar xzf /tmp/pkg.tar.gz -C "$ASDF_DOWNLOAD_PATH" --strip-components=1
Defensive patterns

Strategy: type-guard

Validate before calling

if _, err := os.Stat(filepath.Join(pluginDir, "lib", "utils.bash")); err == nil {
    // plugin defines callbacks; ensure deps like curl/git exist
    if _, err := exec.LookPath("git"); err != nil {
        return fmt.Errorf("download callback needs git: %w", err)
    }
}

Type guard

var nce plugins.NoCallbackError
if errors.As(err, &nce) {
    // optional callback absent — tolerate
} else if err != nil {
    return fmt.Errorf("download failed: %w", err)
}

Try / catch

if err := installtest.InstallOneVersion(conf, plugin, version); err != nil {
    var nce plugins.NoCallbackError
    if errors.As(err, &nce) {
        // tolerated: callback missing
    } else {
        log.Printf("download callback failed: %v", err)
    }
}

Prevention

When it happens

Trigger: Calling InstallOneVersion (used by `asdf install` and plugin test tooling) when: the plugin defines a download callback that exits non-zero; the callback script itself crashes (bad interpreter, syntax error); or RunCallback fails for a non-NoCallbackError reason such as the callback file not being executable or the fork/exec failing.

Common situations: Plugin authors shipping a broken download script; callback referencing missing environment variables or tools (curl, git) not present on the machine; restrictive permissions making the callback non-executable; users installing a plugin whose download URL is dead or behind an auth wall.

Related errors


AI-assisted analysis of asdf-vm/asdf@074a1722ca (2026-08-30). Data as JSON: /api/errors/701c12640be16f9b. Report an issue: GitHub.

Appendix: source

Thrown at internal/installtest/installtest.go:51

	downloadDir := DownloadPath(conf, plugin, version)
	installDir := InstallPath(conf, plugin, version)

	env := map[string]string{
		"ASDF_INSTALL_TYPE":    versionType,
		"ASDF_INSTALL_VERSION": version,
		"ASDF_INSTALL_PATH":    installDir,
		"ASDF_DOWNLOAD_PATH":   downloadDir,
		"ASDF_CONCURRENCY":     "1",
	}

	err = os.MkdirAll(downloadDir, 0o777)
	if err != nil {
		return fmt.Errorf("unable to create download dir: %w", err)
	}

	err = plugin.RunCallback("download", []string{}, env, &stdOut, &stdErr)
	if _, ok := err.(plugins.NoCallbackError); err != nil && !ok {
		return fmt.Errorf("failed to run download callback: %w", err)
	}

	err = os.MkdirAll(installDir, 0o777)
	if err != nil {
		return fmt.Errorf("unable to create install dir: %w", err)
	}

	err = plugin.RunCallback("install", []string{}, env, &stdOut, &stdErr)
	if err != nil {
		return fmt.Errorf("failed to run install callback: %w", err)
	}

	return nil
}

// InstallPath returns the path to a tool installation
func InstallPath(conf config.Config, plugin plugins.Plugin, version string) string {
	return filepath.Join(pluginInstallPath(conf, plugin), formatVersionStringForFS(version))

View on GitHub (pinned to 074a1722ca)