{"record":{"id":"e48e2da80288833c","repo":"ipfs/kubo","slug":"github-api-rate-limit-exceeded-s","errorCode":null,"errorMessage":"GitHub API rate limit exceeded%s","messagePattern":"GitHub API rate limit exceeded(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/commands/update_github.go","lineNumber":97,"sourceCode":"\treq.Header.Set(\"Accept\", \"application/vnd.github+json\")\n\treq.Header.Set(\"User-Agent\", \"kubo/\"+version.CurrentVersionNumber)\n\n\tif token := githubToken(); token != \"\" {\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+token)\n\t}\n\n\tresp, err := http.DefaultClient.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusTooManyRequests {\n\t\tresp.Body.Close()\n\t\thint := \"\"\n\t\tif githubToken() == \"\" {\n\t\t\thint = \" (hint: set GITHUB_TOKEN or GH_TOKEN to avoid rate limits)\"\n\t\t}\n\t\treturn nil, fmt.Errorf(\"GitHub API rate limit exceeded%s\", hint)\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tresp.Body.Close()\n\t\treturn nil, fmt.Errorf(\"GitHub API returned HTTP %d for %s\", resp.StatusCode, url)\n\t}\n\n\treturn resp, nil\n}\n\nfunc githubToken() string {\n\tif t := os.Getenv(\"GITHUB_TOKEN\"); t != \"\" {\n\t\treturn t\n\t}\n\treturn os.Getenv(\"GH_TOKEN\")\n}\n\n// githubLatestRelease returns the newest release that has a platform asset","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/commands/update_github.go#L79-L115","documentation":"githubGet in core/commands/update_github.go treats HTTP 403 or 429 from api.github.com as a GitHub API rate-limit rejection and returns `GitHub API rate limit exceeded` plus a hint when no GITHUB_TOKEN/GH_TOKEN is set. Unauthenticated GitHub requests are limited to ~60/hour per IP, so anonymous update checks exhaust the quota quickly, especially behind shared NAT/CI IPs.","triggerScenarios":"Calling `ipfs update check` / `ipfs update fetch` (via githubListReleases or githubReleaseByTag) more than ~60 times per hour from the same IP without a token; running in CI where the runner IP is shared; or a proxy/firewall returning 403 in a way GitHub surfaces as Forbidden.","commonSituations":"Repeated update checks in automation, a corporate/VPN exit IP already exhausted by others, Docker containers sharing the host IP, or simply forgetting to export GITHUB_TOKEN despite having one.","solutions":["Set a GitHub token: `export GITHUB_TOKEN=ghp_...` (GH_TOKEN also works) to raise the limit to 5000 req/hour.","Wait until the rate-limit window resets (check `curl -s https://api.github.com/rate_limit` for the reset time) and retry.","Avoid polling in loops/scripts; cache the last check result and only query every few hours.","If on shared CI/VPN infrastructure, use a token or a different egress IP."],"exampleFix":"// before\n$ ipfs update check\nError: GitHub API rate limit exceeded (hint: set GITHUB_TOKEN or GH_TOKEN to avoid rate limits)\n// after\n$ export GITHUB_TOKEN=ghp_xxx   # or GH_TOKEN\n$ ipfs update check","handlingStrategy":"retry","validationCode":"// pre-check quota before calling update\nresp, err := http.Get(\"https://api.github.com/rate_limit\")\nif err == nil {\n\tvar rl struct{ Resources struct{ Core struct{ Remaining int; Reset int64 } } }\n\tjson.NewDecoder(resp.Body).Decode(&rl)\n\tif rl.Resources.Core.Remaining == 0 {\n\t\treturn fmt.Errorf(\"GitHub quota exhausted until %v\", time.Unix(rl.Resources.Core.Reset, 0))\n\t}\n}","typeGuard":null,"tryCatchPattern":"rel, err := githubLatestRelease(ctx, false)\nif err != nil {\n\tif strings.Contains(err.Error(), \"rate limit exceeded\") {\n\t\t// honor Retry-After style backoff, or surface the GITHUB_TOKEN hint\n\t\ttime.Sleep(backoff)\n\t\trel, err = githubLatestRelease(ctx, false)\n\t}\n\tif err != nil { return err }\n}","preventionTips":["Always export GITHUB_TOKEN or GH_TOKEN when scripting update checks","Cache the latest-release result and check at most every few hours","Don't run update checks in tight loops or parallel jobs sharing an IP","Monitor X-RateLimit-Remaining if you call the API yourself"],"tags":["github","rate-limit","network","http-403","cli"],"backgroundTag":"api-rate-limit-exceeded","analyzedSha":"329838acdfafae224582930457efe80aa217afc0","analyzedAt":"2026-09-03T18:30:52.135Z","contentChangedAt":"2026-09-03T18:30:52.135Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}