chenhg5/cc-connect · error

parse releases: %w

Error message

parse releases: %w

What it means

Wraps a json.Decode failure while reading the GitHub releases API response in fetchLatestPreRelease. It fires when the API returned a non-JSON body — rate-limit pages, proxy interference, or HTML error responses despite a 200 status.

Source

Thrown at cmd/cc-connect/update.go:234

// fetchLatestPreRelease fetches the newest release (including pre-releases) from GitHub.
func fetchLatestPreRelease() (*githubRelease, error) {
	client := &http.Client{Timeout: 15 * time.Second}
	req, _ := http.NewRequest("GET", githubAllAPI+"?per_page=10", nil)
	req.Header.Set("Accept", "application/vnd.github.v3+json")

	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("GitHub API returned HTTP %d", resp.StatusCode)
	}

	var releases []githubRelease
	if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
		return nil, fmt.Errorf("parse releases: %w", err)
	}

	if len(releases) == 0 {
		return nil, fmt.Errorf("no releases found")
	}

	// Return the first (newest) release, which may be a pre-release
	return &releases[0], nil
}

// fetchLatestStableRelease fetches the latest stable release (no pre-releases).
func fetchLatestStableRelease() (*githubRelease, error) {
	client := &http.Client{Timeout: 15 * time.Second}
	req, _ := http.NewRequest("GET", githubAPI, nil)
	req.Header.Set("Accept", "application/vnd.github.v3+json")

	resp, err := client.Do(req)
	if err == nil {

View on GitHub (pinned to 4000b2338a)

Solutions

  1. curl the endpoint and inspect the body — if it's HTML, a proxy is intercepting; fix proxy/bypass settings
  2. Verify githubAllAPI is the /releases array endpoint, not /releases/latest (which returns an object)
  3. Check the wrapped %w text: 'invalid character <' means HTML; 'cannot unmarshal object' means wrong endpoint shape
  4. Retry — truncated bodies on unstable networks resolve on retry

Example fix

// before
if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
    return nil, fmt.Errorf("parse releases: %w", err)
}
// after
body, _ := io.ReadAll(resp.Body)
if strings.HasPrefix(strings.TrimSpace(string(body)), "<") {
    return nil, fmt.Errorf("non-JSON body (proxy interception?): %.100s", body)
}
if err := json.Unmarshal(body, &releases); err != nil {
    return nil, fmt.Errorf("parse releases: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

// validate the endpoint returns JSON before relying on it:
resp, _ := http.Get(githubAllAPI + "?per_page=1")
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
    return errors.New("endpoint did not return JSON: " + ct)
}

Try / catch

// Go: capture diagnosis on parse failure
if _, err := fetchRelease(); err != nil {
    if strings.Contains(err.Error(), "parse releases") {
        log.Printf("release JSON parse failed; inspect raw response from %s for HTML/proxy interference", githubAllAPI)
    }
}

Prevention

When it happens

Trigger: json.NewDecoder(resp.Body).Decode(&releases) errors: body is HTML (proxy/captive portal returned 200 with an error page), truncated response, or the API endpoint URL is wrong and returns a different JSON shape (object instead of array).

Common situations: Corporate proxies injecting HTML block pages with status 200; mistyped githubAllAPI pointing at an endpoint that returns a JSON object (e.g. /releases/latest) instead of an array; GitHub returning partial/truncated bodies on flaky connections.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/c1ae6fd8e0dc5871. Report an issue: GitHub.