{"record":{"id":"c1ae6fd8e0dc5871","repo":"chenhg5/cc-connect","slug":"parse-releases-w","errorCode":null,"errorMessage":"parse releases: %w","messagePattern":"parse releases: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/cc-connect/update.go","lineNumber":234,"sourceCode":"// fetchLatestPreRelease fetches the newest release (including pre-releases) from GitHub.\nfunc fetchLatestPreRelease() (*githubRelease, error) {\n\tclient := &http.Client{Timeout: 15 * time.Second}\n\treq, _ := http.NewRequest(\"GET\", githubAllAPI+\"?per_page=10\", nil)\n\treq.Header.Set(\"Accept\", \"application/vnd.github.v3+json\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"request failed: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != 200 {\n\t\treturn nil, fmt.Errorf(\"GitHub API returned HTTP %d\", resp.StatusCode)\n\t}\n\n\tvar releases []githubRelease\n\tif err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse releases: %w\", err)\n\t}\n\n\tif len(releases) == 0 {\n\t\treturn nil, fmt.Errorf(\"no releases found\")\n\t}\n\n\t// Return the first (newest) release, which may be a pre-release\n\treturn &releases[0], nil\n}\n\n// fetchLatestStableRelease fetches the latest stable release (no pre-releases).\nfunc fetchLatestStableRelease() (*githubRelease, error) {\n\tclient := &http.Client{Timeout: 15 * time.Second}\n\treq, _ := http.NewRequest(\"GET\", githubAPI, nil)\n\treq.Header.Set(\"Accept\", \"application/vnd.github.v3+json\")\n\n\tresp, err := client.Do(req)\n\tif err == nil {","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/cmd/cc-connect/update.go#L216-L252","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["curl the endpoint and inspect the body — if it's HTML, a proxy is intercepting; fix proxy/bypass settings","Verify githubAllAPI is the /releases array endpoint, not /releases/latest (which returns an object)","Check the wrapped %w text: 'invalid character <' means HTML; 'cannot unmarshal object' means wrong endpoint shape","Retry — truncated bodies on unstable networks resolve on retry"],"exampleFix":"// before\nif err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {\n    return nil, fmt.Errorf(\"parse releases: %w\", err)\n}\n// after\nbody, _ := io.ReadAll(resp.Body)\nif strings.HasPrefix(strings.TrimSpace(string(body)), \"<\") {\n    return nil, fmt.Errorf(\"non-JSON body (proxy interception?): %.100s\", body)\n}\nif err := json.Unmarshal(body, &releases); err != nil {\n    return nil, fmt.Errorf(\"parse releases: %w\", err)\n}","handlingStrategy":"validation","validationCode":"// validate the endpoint returns JSON before relying on it:\nresp, _ := http.Get(githubAllAPI + \"?per_page=1\")\nct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n    return errors.New(\"endpoint did not return JSON: \" + ct)\n}","typeGuard":null,"tryCatchPattern":"// Go: capture diagnosis on parse failure\nif _, err := fetchRelease(); err != nil {\n    if strings.Contains(err.Error(), \"parse releases\") {\n        log.Printf(\"release JSON parse failed; inspect raw response from %s for HTML/proxy interference\", githubAllAPI)\n    }\n}","preventionTips":["Bypass proxies for api.github.com or configure NO_PROXY","Check Content-Type of API responses before decoding","Handle rate-limit/error JSON shapes explicitly instead of blind decoding","Use a bounded io.LimitReader so truncated/oversized bodies fail fast with context"],"tags":["json","github-api","proxy","update-checker","response-parsing"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}