asdf-vm/asdf · error

unable to create download dir: %w

Error message

unable to create download dir: %w

What it means

InstallOneVersion creates a per-install download directory (conf.DataDir/downloads/<plugin>/<version>) and exports it as ASDF_DOWNLOAD_PATH before running download hooks/callbacks. If os.MkdirAll fails — permissions, disk full, path is a file — installation aborts with this wrapped error before anything is downloaded.

Source

Thrown at internal/versions/versions.go:174

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

	if installs.IsInstalled(conf, plugin, version) {
		return VersionAlreadyInstalledError{version: version, toolName: plugin.Name}
	}

	concurrency, _ := conf.Concurrency()
	env := map[string]string{
		"ASDF_INSTALL_TYPE":    version.Type,
		"ASDF_INSTALL_VERSION": version.Value,
		"ASDF_INSTALL_PATH":    installDir,
		"ASDF_DOWNLOAD_PATH":   downloadDir,
		"ASDF_CONCURRENCY":     concurrency,
	}

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

	err = hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_download_%s", plugin.Name), []string{version.Value}, stdOut, stdErr)
	if err != nil {
		return fmt.Errorf("failed to run pre-download hook: %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 = hook.RunWithOutput(conf, fmt.Sprintf("pre_asdf_install_%s", plugin.Name), []string{version.Value}, stdOut, stdErr)
	if err != nil {
		return fmt.Errorf("failed to run pre-install hook: %w", err)
	}

	err = os.MkdirAll(installDir, 0o777)

View on GitHub (pinned to 074a1722ca)

Solutions

  1. Check disk space (df -h) and free space if full
  2. Fix ownership/permissions on ASDF_DATA_DIR and its downloads/ tree
  3. Ensure ASDF_DATA_DIR is on a writable mount
  4. Remove any non-directory file occupying the download path
Defensive patterns

Strategy: validation

Validate before calling

if err := os.MkdirAll(downloadDir, 0o777); err != nil {
    return fmt.Errorf("download dir not writable: %w", err)
}
if free, err := diskFree(dataDir); err == nil && free < minNeeded {
    return fmt.Errorf("insufficient disk space")
}

Try / catch

if err != nil && strings.Contains(err.Error(), "unable to create download dir") {
    // check disk space / permissions, then retry install
}

Prevention

When it happens

Trigger: os.MkdirAll(downloadDir, 0o777) returns an error: parent dirs unwritable, ASDF_DATA_DIR on a read-only mount, disk full, or a non-directory already exists at the download path.

Common situations: Read-only or full filesystem, running asdf as a different user than the data dir owner, ASDF_DATA_DIR pointing at a restricted path, leftover file where the downloads dir should be.

Related errors


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