coreybutler/nvm-windows · error

error: parsing release: %v

Error message

error: parsing release: %v

What it means

checkForUpdate unmarshals the fetched release JSON into a Release struct. This error means json.Unmarshal failed: the body is not valid JSON or does not match expectations (wrong types, HTML error page parsed as JSON). Classic causes are a proxy returning an HTML block page with 200, a mirror serving a different schema, or the URL pointing at an HTML page instead of the API JSON.

Source

Thrown at src/upgrade/upgrade.go:749

	return io.ReadAll(resp.Body)
}

func checkForUpdate(url string) (*Update, error) {
	u := Update{Assets: []string{}, Warnings: []string{}, VersionWarnings: []string{}}
	r := Release{}

	// Make the HTTP GET request
	utility.DebugLogf("checking for updates at %s", url)
	body, err := get(url, false)
	if err != nil {
		return &u, fmt.Errorf("error: reading response body: %v", err)
	}

	// Parse JSON into the struct
	utility.DebugLogf("Received:\n%s", string(body))
	err = json.Unmarshal(body, &r)
	if err != nil {
		return &u, fmt.Errorf("error: parsing release: %v", err)
	}

	u.Version = r.Version
	utility.DebugLogf("latest version: %s", u.Version)

	// Comment the next line when development is complete
	// u.Version = "2.0.0"
	for _, asset := range r.Assets {
		if value, exists := asset["name"]; exists && value.(string) == "update.exe" {
			u.Assets = append(u.Assets, value.(string))
		}
		if value, exists := asset["name"]; exists && value.(string) == "nvm-noinstall.zip" {
			u.SourceURL = asset["browser_download_url"].(string)
		}
	}

	utility.DebugLogf("source URL: %s", u.SourceURL)
	utility.DebugLogf("assets: %v", u.Assets)

View on GitHub (pinned to 5b18223ca1)

Solutions

  1. Fetch the update URL manually and pipe to a JSON validator to see the actual payload.
  2. If a proxy injects HTML, bypass it or authenticate; the body seen in the debug log (DebugLogf 'Received:') shows exactly what came back.
  3. Point the update-check URL back at the official GitHub API JSON endpoint.
  4. Upgrade nvm-windows manually to a version matching the current feed format.
Defensive patterns

Strategy: validation

Validate before calling

// Sanity-check the payload before unmarshal
trimmed := bytes.TrimSpace(body)
if len(trimmed) == 0 || trimmed[0] != '{' {
    return &u, fmt.Errorf("update feed returned non-JSON (likely proxy/captive portal), first byte %q", trimmed[0])
}

Try / catch

Catch, log the raw body prefix (already done via DebugLogf 'Received:'), and skip the update — never fall back to guessing a version. Distinguish proxy-HTML responses from schema drift in the message.

Prevention

When it happens

Trigger: Captive portal / proxy returning HTML with status 200; update URL misconfigured to a releases web page instead of the JSON API endpoint; a mirror rewriting the response; truncated body from a flaky connection; upstream schema change where a field changes type and the strict struct rejects it.

Common situations: Hotel/airport captive portals intercepting the update check; NVM update URL overridden to a third-party mirror with a different feed format; GitHub incident serving error pages; new release format after an nvm-windows version change.

Related errors


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