[BUG]: Secondary rate limits are filed as primary ones, losing the rest of the hour
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- github, go, terraform
- Domain
- api, backend-api-design
Research direction
Start at internal/ghclient/transport.go:76 and inspect the go-github-ratelimit dependency declaration. Bump the dependency past the release containing gofri/go-github-ratelimit#51, then verify that secondaryRateLimitCallback handles burst limits and that responses without retry-after are passed through as described. Done means secondary limits no longer arm the primary resource category.
Written by the indexing model from the issue text.
Description
Expected Behavior
A secondary (burst) rate limit is backed off for the few seconds it asks to be waited out, and the run continues. The primary hourly quota — which has not been exhausted — stays usable.
Actual Behavior
One secondary rate limit ends the run. Every subsequent request in the same resource category is refused locally, without being sent, until the primary window resets up to an hour later:
Error: ... (http.RoundTripper).RoundTrip failed: primary rate limit reached on request to
https://api.github.com/... with category: core. wait until ... before sending more requests.
From a single terraform plan against an organisation of ~200 repositories, authenticated as a GitHub App installation (12,500 requests/hour):
288 identical errors, all timestamped within the same second, all citing the same reset ~9 minutes out
primary quota consumed across the entire run: 210 of 12,500 (1.7%)
before 12,498 / 12,500
after 12,288 / 12,500
A 12,500/hour primary limit cannot be exhausted by 210 requests. The run never came close to its primary budget. The 288-errors-in-one-second shape is not 288 rejections from GitHub — it is the category being armed once and every in-flight request being refused locally thereafter.
Root cause
Not in the provider. It is in github.com/gofri/go-github-ratelimit/v2 v2.0.2, wired up at internal/ghclient/transport.go:76.
GitHub sends x-ratelimit-remaining: 0 on some secondary rate limits, so the status code and that header together do not distinguish the two kinds of limit. Both of that library's limiters nevertheless decide on the header alone, from opposite directions:
- the primary limiter arms the whole resource category for any 403/429 carrying
remaining == 0, without reading the body; - the secondary detector returns early — commented
// a primary rate limit— on that same header, before reading the body that would have identified it.
The secondary detector's deferral would be harmless if the primary limiter got it right, but github_ratelimit.New stacks the secondary limiter outside the primary one, so the primary inspects the response first, arms the category and returns an error. The secondary limiter never gets the turn the deferral assumed it would.
So a burst throttle asking for a few seconds' backoff is filed as an exhausted hourly quota, and the rest of the window is lost.
Upstream issue: gofri/go-github-ratelimit#50 — fix proposed in gofri/go-github-ratelimit#51 (makes the response body, rather than the header, decide which limit it is, on both sides).
What the provider needs to do once the fix is available
Bump github.com/gofri/go-github-ratelimit/v2 past the release carrying gofri/go-github-ratelimit#51. No provider code change — internal/ghclient/transport.go already passes the responses through both limiters correctly; they were classifying them wrongly.
Two things worth verifying with the bump, since this changes which limiter handles a response:
secondaryRateLimitCallbackwill start firing for responses that previously firedprimaryRateLimitCallback. That is the intended correction, but it means the tflog warnings change shape for anyone matching on them.- A secondary limit carrying
remaining: 0and noretry-afterhas no usable backoff duration and is passed through to the caller as a bare 403 rather than waited out. That is a pre-existing gap in the library (theXXXinparseSecondaryLimitTime), not a new one, but it becomes reachable for this response shape. Worth a look once the bump lands.
Related but distinct: #3565 asks for the provider to wait for a primary reset rather than failing. That needs library support too — gofri/go-github-ratelimit#46, proposed in gofri/go-github-ratelimit#52 — and I have commented there with the details. The two are ordered: waiting is only safe once a primary limit actually means a primary limit, or a misclassified burst throttle turns a fail-fast into an hour-long sleep.
Terraform Version
Terraform v1.x, provider v6.13.0
GitHub Installation Type
GitHub App installation (app_auth), GitHub.com
Affected Resource(s)
All — this is in the shared HTTP transport, so any resource or data source can be the one that trips it.
Steps to Reproduce
Any plan or apply large enough to trigger GitHub's burst/secondary throttle. It needs concurrency rather than volume: ~200 repositories with several resources each was enough, well inside the primary budget.
Code of Conduct
- I agree to follow this project's Code of Conduct
- Dominant language
- Go
- Stars
- 1.2k
- Forks
- 1.1k
- Avg merge
- 9h 41m
- Merged PRs (30d)
- 3
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from integrations/terraform-provider-github
-
[DOCS]: enforce_admins description understates what it covers and omits push restrictionsPossibly taken @dav-tb claimed this 1 day ago. Open
Difficulty 1/5 Under an hour Newbie friendliness 78/100
integrations/terraform-provider-github#3707 · 1 comment ·
Maintainers usually reply within 1 day
-
[FEAT]: Add ruleset bypass actor type `OrganizationRole`Possibly taken @marcencov claimed this 15 days ago. OpenStatus: Triage Type: Feature
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
integrations/terraform-provider-github#3674 · 3 comments · 1 reaction ·
Maintainers usually reply within 1 day
-
github_organization_custom_properties Read doesn't handle 404, breaking recovery after deletionPossibly taken @mkushakov claimed this 245 days ago. Openr/organization_custom_properties Type: Bug
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
integrations/terraform-provider-github#3641 · 1 comment ·
Maintainers usually reply within 1 day
-
Status: Triage Type: Feature
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
integrations/terraform-provider-github#3613 · 1 comment · 3 reactions ·
Maintainers usually reply within 1 day
-
[BUG]: Out-of-band removal of environment reviewers is not detected on refreshPossibly taken @nitinjain999 claimed this 69 days ago. Openr/repository_environment Type: Bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
integrations/terraform-provider-github#3609 · 3 comments · 1 reaction ·
Maintainers usually reply within 1 day
All issues in integrations/terraform-provider-github
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
microcks/microcks-cli#608 · 1 comment ·
Maintainers usually reply within 1 day
-
bug from-studio
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
esengine/DeepSeek-Reasonix#14868 ·
Maintainers usually reply within 1 day
-
Install notes print "Camunda REST API: https://" without a host when global.host is setPossibly taken @irisfield claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
camunda/camunda-platform-helm#7790 ·
Maintainers usually reply within 1 day
-
[Bug]: zot exits with code 0 when it can't startPossibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
project-zot/zot#4636 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
keyxmakerx/Chronicle#1911 ·
Maintainers usually reply within 1 day