coreybutler/nvm-windows · error

error: failed to download asset: %v

Error message

error: failed to download asset: %v

What it means

During upgrade, each extra entry in update.Assets (e.g. update.exe) is fetched with get(). This error means one of those per-asset downloads failed. Note the loop sends the error to the status channel but does not break or return, so the upgrade continues with a missing asset unless the consumer aborts — the missing file surfaces later as a copy or exec failure.

Source

Thrown at src/upgrade/upgrade.go:443

	status <- Status{Text: "extracting update..."}
	if err := unzip(filepath.Join(tmp, "assets.zip"), filepath.Join(tmp, "assets")); err != nil {
		status <- Status{Err: err}
	}

	// Get any additional assets
	if len(update.Assets) > 0 {
		status <- Status{Text: fmt.Sprintf("downloading %d additional assets...", len(update.Assets))}
		for _, asset := range update.Assets {
			var assetURL string
			if !strings.HasPrefix(asset, "http") {
				assetURL = update.SourceURL
				// assetURL = fmt.Sprintf(update.SourceURL, asset)
			} else {
				assetURL = asset
			}
			assetBody, err := get(assetURL)
			if err != nil {
				status <- Status{Err: fmt.Errorf("error: failed to download asset: %v\n", err)}
			}

			assetPath := filepath.Join(tmp, "assets", asset)
			os.WriteFile(assetPath, assetBody, os.ModePerm)
		}
	}

	// Debugging
	if verbose {
		tree(tmp, "downloaded files (extracted):")
		nvmtestcmd := exec.Command(filepath.Join(tmp, "assets", "nvm.exe"), "version")
		nvmtestcmd.Stdout = os.Stdout
		nvmtestcmd.Stderr = os.Stderr
		err = nvmtestcmd.Run()
		if err != nil {
			fmt.Println("error running nvm.exe:", err)
		}
	}

View on GitHub (pinned to 5b18223ca1)

Solutions

  1. Identify which asset failed (enable verbose/debug logging) and fetch its URL manually to see the status code.
  2. Retry the upgrade — transient failures on the small asset downloads are common.
  3. Check proxy rules cover every host in the release manifest, not just the main download host.
  4. Fix the release manifest if the asset name/URL is genuinely wrong upstream.

Example fix

// before
assetBody, err := get(assetURL)
if err != nil {
    status <- Status{Err: fmt.Errorf("error: failed to download asset: %v\n", err)}
}

// after: abort the loop on failure so the upgrade does not continue with missing files
assetBody, err := get(assetURL)
if err != nil {
    status <- Status{Err: fmt.Errorf("error: failed to download asset %s: %v\n", assetURL, err)}
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// Resolve and validate every asset URL before the upgrade starts
for _, asset := range update.Assets {
    u := asset
    if !strings.HasPrefix(u, "http") {
        u = update.SourceURL
    }
    if resp, err := http.Head(u); err != nil || resp.StatusCode != http.StatusOK {
        return fmt.Errorf("asset not fetchable: %s", u)
    }
}

Try / catch

On any asset download error, abort the whole upgrade (return) — continuing leaves a half-updated tree. Distinguish 404 (manifest broken upstream) from transport errors (retry) in the message.

Prevention

When it happens

Trigger: Asset URLs listed in the release manifest that 404 (renamed asset); non-http asset names resolved against update.SourceURL that produce an invalid URL; network drop mid-loop after the main zip succeeded; proxy allowing the zip host but blocking the asset host.

Common situations: GitHub release edited between publishing the manifest and assets; mixed hosts where update.exe sits on a CDN the firewall blocks; transient failure on the Nth of several sequential downloads.

Related errors


AI-assisted analysis of coreybutler/nvm-windows@5b18223ca1 (2026-08-15). Data as JSON: /api/errors/c07fdddb7b664b63. Report an issue: GitHub.