{"record":{"id":"650eee63f033ff3a","repo":"chenhg5/cc-connect","slug":"api-returned-d","errorCode":null,"errorMessage":"API returned %d","messagePattern":"API returned (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/updater.go","lineNumber":115,"sourceCode":"}\n\nfunc fetchReleasesFrom(apiURL string) ([]ReleaseInfo, error) {\n\tclient := &http.Client{Timeout: 15 * time.Second}\n\treq, err := http.NewRequest(\"GET\", apiURL, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"User-Agent\", \"cc-connect-updater\")\n\treq.Header.Set(\"Accept\", \"application/json\")\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"API returned %d\", resp.StatusCode)\n\t}\n\n\tvar releases []ReleaseInfo\n\tif err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {\n\t\treturn nil, err\n\t}\n\treturn releases, nil\n}\n\n// SelfUpdate downloads and installs the given release version.\n// If preferGitee is true, tries Gitee download first.\nfunc SelfUpdate(tag string, preferGitee bool) error {\n\tgoos := runtime.GOOS\n\tgoarch := runtime.GOARCH\n\n\text := \".tar.gz\"\n\tif goos == \"windows\" {\n\t\text = \".zip\"","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/updater.go#L97-L133","documentation":"fetchReleasesFrom (core/updater.go:115) treats any non-200 HTTP status from the releases API as an error and returns `API returned <status>`. The library expects the GitHub/Gitee releases endpoint to reply 200 with a JSON array; anything else (403 rate limit, 404 bad repo, 5xx outage) aborts that source. On the primary source this triggers the fallback; on the fallback it surfaces inside error 915.","triggerScenarios":"The GitHub or Gitee releases API responds with a status other than 200: GitHub API rate limiting (403, unauthenticated limit of 60 req/h per IP), repository renamed/removed (404), server-side 5xx, or Gitee returning 404 for the API path. Produced whenever fetchReleasesFrom is called by fetchReleases during CheckForUpdate.","commonSituations":"CI servers or office NATs sharing one IP exhausting the unauthenticated GitHub rate limit; typos or renamed org/repo making the endpoint 404; temporary GitHub/Gitee incidents; mainland-China networks where api.github.com returns errors or is intercepted.","solutions":["Read the numeric status in the message: 403 = rate-limited (wait for the hourly reset or authenticate), 404 = wrong repo path, 5xx = server-side, retry later.","Check `curl -sS -o /dev/null -w '%{http_code}' https://api.github.com/repos/chenhg5/cc-connect/releases?per_page=1` from the affected machine to reproduce.","If 403 rate-limited, set preferGitee=true so checks hit Gitee instead, or reduce check frequency.","Retry after a few minutes if it's a transient 5xx; the updater already falls back to the other source automatically.","If 404 persists, verify the repo/API URL constants match the actual repository."],"exampleFix":"// before: status only, no body\nif resp.StatusCode != http.StatusOK {\n\treturn nil, fmt.Errorf(\"API returned %d\", resp.StatusCode)\n}\n\n// after: include URL and body hint for diagnosis\nif resp.StatusCode != http.StatusOK {\n\tbody, _ := io.ReadAll(io.LimitReader(resp.Body, 512))\n\treturn nil, fmt.Errorf(\"API returned %d for %s: %s\", resp.StatusCode, apiURL, body)\n}","handlingStrategy":"retry","validationCode":"func releasesAPIHealthy() error {\n\tc := &http.Client{Timeout: 10 * time.Second}\n\treq, _ := http.NewRequest(\"GET\", \"https://api.github.com/repos/chenhg5/cc-connect/releases?per_page=1\", nil)\n\treq.Header.Set(\"User-Agent\", \"cc-connect-updater\")\n\tresp, err := c.Do(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer resp.Body.Close()\n\tif resp.StatusCode == 403 {\n\t\treturn errors.New(\"GitHub API rate limited; use Gitee or wait for reset\")\n\t}\n\tif resp.StatusCode != 200 {\n\t\treturn fmt.Errorf(\"releases API status %d\", resp.StatusCode)\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":"releases, err := core.CheckForUpdate(version, preferGitee)\nif err != nil {\n\tif strings.Contains(err.Error(), \"API returned 403\") {\n\t\t// rate-limited: back off and retry later\n\t\ttime.Sleep(time Until next hour)\n\t} else if strings.Contains(err.Error(), \"API returned 5\") {\n\t\t// transient server error: retry with backoff\n\t}\n}","preventionTips":["Keep update-check frequency low; unauthenticated GitHub API allows only 60 req/h per IP.","Switch to preferGitee=true when you see 403s from shared egress IPs.","Monitor GitHub/Gitee status pages when many users report the error at once.","Verify repo path constants if a 404 persists after a repo rename.","Handle 5xx as transient and rely on the built-in source fallback."],"tags":["http","github-api","gitee","rate-limit","updater"],"backgroundTag":"http-error-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}