{"record":{"id":"b580edb160485664","repo":"multica-ai/multica","slug":"github-api-returned-d","errorCode":null,"errorMessage":"GitHub API returned %d","messagePattern":"GitHub API returned (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/cli/update.go","lineNumber":242,"sourceCode":"\treturn nil\n}\n\nfunc fetchReleaseByTag(tag string) (*GitHubRelease, error) {\n\tclient := &http.Client{Timeout: 10 * time.Second}\n\treq, err := http.NewRequest(http.MethodGet, \"https://api.github.com/repos/multica-ai/multica/releases/tags/\"+tag, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Accept\", \"application/vnd.github+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(\"GitHub API returned %d\", resp.StatusCode)\n\t}\n\n\tvar release GitHubRelease\n\tif err := json.NewDecoder(resp.Body).Decode(&release); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &release, nil\n}\n\n// FetchLatestRelease fetches the latest release tag from the multica GitHub repo.\nfunc FetchLatestRelease() (*GitHubRelease, error) {\n\tclient := &http.Client{Timeout: 10 * time.Second}\n\treq, err := http.NewRequest(http.MethodGet, \"https://api.github.com/repos/multica-ai/multica/releases/latest\", nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treq.Header.Set(\"Accept\", \"application/vnd.github+json\")\n","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/cli/update.go#L224-L260","documentation":"fetchReleaseByTag called GET https://api.github.com/repos/multica-ai/multica/releases/tags/<tag> and got a non-200 response. The status code is in the message: 404 means no release with that tag, 403/429 mean GitHub API rate limiting (60 req/h unauthenticated), 401 means bad credentials if a token was attached.","triggerScenarios":"Requesting a release tag that does not exist (typo, 'v' prefix mismatch, draft release invisible to unauthenticated calls), exhausting the unauthenticated rate limit from repeated update polls, or GitHub incidents returning 5xx.","commonSituations":"Auto-update pollers on many machines sharing one NAT IP burning the 60/h anonymous quota; specifying version '1.2.3' when the tag is 'v1.2.3'; drafts/pre-releases not matching the tags endpoint.","solutions":["If 404: verify the exact tag exists on the releases page (mind the leading 'v')","If 403/429: set GITHUB_TOKEN (or the repo's configured token) to raise the rate limit to 5000/h, and lengthen the poll interval","If 5xx: check githubstatus.com and retry later","Cache the latest-release result so each machine does not hit the API on every poll"],"exampleFix":"# before: unauthenticated polling hits the 60 req/h limit\n$ multica update check\nGitHub API returned 403\n\n# after\n$ export GITHUB_TOKEN=ghp_xxx\n$ multica update check","handlingStrategy":"retry","validationCode":"if token := os.Getenv(\"GITHUB_TOKEN\"); token == \"\" {\n\t// unauthenticated GitHub API is limited to 60 req/h; set a token for pollers\n}","typeGuard":null,"tryCatchPattern":"rel, err := cli.FetchReleaseByTag(tag) // or equivalent\nif err != nil {\n\tif strings.Contains(err.Error(), \"GitHub API returned 404\") {\n\t\t// tag does not exist; correct the version string\n\t} else if strings.Contains(err.Error(), \"403\") || strings.Contains(err.Error(), \"429\") {\n\t\t// rate limited: back off and retry with a token\n\t}\n}","preventionTips":["Set GITHUB_TOKEN on machines that poll for updates","Back off on 403/429 and cache release lookups between polls","Verify tag names (leading 'v') before requesting by tag"],"tags":["network","github-api","rate-limit","http","go"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}