You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
One owner token's rate limit throttles or pauses every owner because GitHub keeps a single provider-wide budget #331
The GitHub provider keeps a single rate-limit budget (RateLimitBudgetRemaining/Limit/ResetTime, Status, RateLimitResetTime in BuildProvider.cs). Each request, though, goes out with either the owner's override token or the provider token (GetClientForOwner, Providers/GitHub.cs:52; SetCurrentClient(owner) in MakeGitHubRequestAsync, GitHub.cs:673-699).
GitHub rate limits are per account. Owner tokens exist to reach orgs the provider token can't, so they usually belong to a different account: an EMU user, a bot or machine user, or another identity.
After every successful request, UpdateRateLimitFromApiInfo() (GitHub.cs:699, 784) overwrites the shared budget with the numbers from whichever token answered last. Every later request, for every owner, is then paced and prioritised against that number:
GetRateLimitWaitTime() / CalculateAdaptivePacing() in BuildProvider.cs
OnRateLimitExceeded on a 403/429 (GitHub.cs:728, 741), which sets the provider-wide RateLimited status and reset time
Failure scenario
The provider token has 4,800 of 5,000 requests left. An owner token for org acme is a separate account that CI also uses; it has 100 of 5,000 left, and its window resets in 40 minutes.
A request for acme succeeds. The provider budget now reads 100/5,000 (2%).
IsCriticalBudget becomes true, so MaxAllowedPriority = High. Discovery is skipped for all owners, and completed or failed builds of every other owner stop polling.
Adaptive pacing works out as follows:
reserved budget is 500, so usable budget is max(1, 100 − 500) = 1
the computed delay is therefore clamped to 30 s
every request for every owner sleeps 30 s inside MakeGitHubRequestAsync while holding one of the RequestSemaphore slots, so the UI refresh crawls
The next request sent with the provider token flips the budget back to 4,800. The status bar's "x/5000" keeps alternating between the two accounts.
If the acme token is actually exhausted, its 403 puts the whole provider into RateLimited with acme's reset time. GetRateLimitWaitTime then delays every owner's requests by up to 10 minutes, even though the provider token still has plenty of budget.
Why it matters
One busy owner token degrades or freezes monitoring for every other owner. The status bar also shows a budget that belongs to neither token consistently.
Suggested fix / acceptance criteria
Track rate-limit budget, RateLimited state and reset time per token: one entry for the provider token and one per owner override.
Pacing, the MaxAllowedPriority check and discovery gating use the budget of the token the request will actually be sent with.
A rate limit or near-exhaustion on one owner's token does not delay or deprioritise requests sent with the provider token or another owner's token.
The status bar shows the provider token's budget. The tooltip also shows owner-token budgets where they differ.
Add a unit test with two budgets, one nearly exhausted, asserting that the other owner's MaxAllowedPriority and wait time are unaffected.
Priority: Medium. With owner override tokens from a different account, one nearly exhausted token throttles, deprioritises or pauses polling for every owner, and the status-bar budget flips between accounts. It only affects setups that use per-owner tokens, but there it can stall monitoring for up to 10 minutes at a time.
Area:BuildProvider.cs rate-limit state and pacing, and Providers/GitHub.cs (GetClientForOwner, MakeGitHubRequestAsync, UpdateRateLimitFromApiInfo). The budget needs to be keyed per token. Suggested owner: @matt-edmondson
Activity
matt-edmondson commentedon Oct 5, 2026
Triage
BuildProvider.csrate-limit state and pacing, andProviders/GitHub.cs(GetClientForOwner,MakeGitHubRequestAsync,UpdateRateLimitFromApiInfo). The budget needs to be keyed per token. Suggested owner: @matt-edmondsonGenerated by Claude Code