getJson in packages/cli/src/contributions.ts maps every 403 the same way:
if (response.status === 403 || response.status === 429) {
throw new Error(
`GitHub rate limit hit while reading ${url}. Set GITHUB_TOKEN to raise the anonymous limit.`,
);
}
GitHub returns 403 for far more than rate limiting: insufficient token scopes ("Resource not accessible by integration"), a token that is not allowed to read a private repo, SSO/org enforcement, and abuse-detection limits (which is 429, or 403 with retry-after). Telling an operator to "set GITHUB_TOKEN" when they already have one set — the exact situation this repo's own tooling note describes — sends them down the wrong path, and the real message from GitHub is discarded.
Fix: distinguish the cases, e.g. read x-ratelimit-remaining / retry-after headers (already available on the response) and the message field of the JSON body, and only claim "rate limit" when GitHub actually says so. The current FetchLike type in this module has no headers, so widen it — and keep the "no token set" hint only for genuinely anonymous rate limits.
getJsoninpackages/cli/src/contributions.tsmaps every 403 the same way:GitHub returns 403 for far more than rate limiting: insufficient token scopes ("Resource not accessible by integration"), a token that is not allowed to read a private repo, SSO/org enforcement, and abuse-detection limits (which is 429, or 403 with
retry-after). Telling an operator to "set GITHUB_TOKEN" when they already have one set — the exact situation this repo's own tooling note describes — sends them down the wrong path, and the real message from GitHub is discarded.Fix: distinguish the cases, e.g. read
x-ratelimit-remaining/retry-afterheaders (already available on the response) and themessagefield of the JSON body, and only claim "rate limit" when GitHub actually says so. The currentFetchLiketype in this module has noheaders, so widen it — and keep the "no token set" hint only for genuinely anonymous rate limits.