can1357/oh-my-pi · error · Error
GitHub API rate limit exceeded while fetching release metada
Error message
GitHub API rate limit exceeded while fetching release metadata; retry later or set GITHUB_TOKEN or GH_TOKEN
What it means
GitHub rate-limits unauthenticated API requests (~60/hour per IP) and authenticated ones more generously. When the metadata request returns 429, or 403 while no GITHUB_TOKEN/GH_TOKEN is set (GitHub signals rate limiting via 403 for anonymous clients), the updater throws this message instead of the generic non-ok error so the remedy is obvious.
Source
Thrown at packages/coding-agent/src/cli/update-cli.ts:272
"X-GitHub-Api-Version": "2022-11-28",
};
if (githubToken) headers.Authorization = `Bearer ${githubToken}`;
let response: Response;
try {
response = await fetchImpl(`${GITHUB_API}/repos/${REPO}/releases/tags/${encodeURIComponent(tag)}`, {
headers,
signal: withTimeoutSignal(RELEASE_METADATA_TIMEOUT_MS),
});
} catch (err) {
if (isTimeoutError(err)) {
throw new Error("Timed out fetching GitHub release metadata after 30s", { cause: err });
}
if (isUnsupportedProxyError(err)) throw new Error(unsupportedProxyMessage(), { cause: err });
throw err;
}
if ((response.status === 403 && !githubToken) || response.status === 429) {
throw new Error(
"GitHub API rate limit exceeded while fetching release metadata; retry later or set GITHUB_TOKEN or GH_TOKEN",
);
}
if (!response.ok) {
throw new Error(`Failed to fetch GitHub release metadata: ${response.statusText}`);
}
return resolveReleaseBinaryAsset(await response.json(), tag, binaryName, { allowPrerelease });
}
export interface VerifiedBinaryDownloadOptions {
url: string;
targetPath: string;
expectedSize: number;
expectedDigest: string;
fetchImpl?: Fetch;
}
View on GitHub (pinned to 9690622007)
Solutions
- Set a GitHub token: export GITHUB_TOKEN=<personal access token> (GH_TOKEN also accepted), then retry.
- Wait for the rate-limit window to reset (check X-RateLimit-Reset header); anonymous limits reset hourly.
- Retry later if on shared CI infrastructure — or configure a token as a CI secret.
- Reduce polling frequency if a script is hitting the endpoint repeatedly.
Example fix
// before omp update // after export GITHUB_TOKEN=ghp_xxxxxxxxxxxx omp update
Defensive patterns
Strategy: retry
Validate before calling
const check = await fetch(`https://api.github.com/rate_limit`);
const { rate } = await check.json();
if (rate.remaining === 0) {
console.error(`GitHub rate limit exhausted; resets at ${new Date(rate.reset * 1000).toISOString()}.`);
process.exit(1);
} Try / catch
try {
await update();
} catch (err) {
if (err instanceof Error && err.message.includes("rate limit exceeded")) {
console.error("Set GITHUB_TOKEN to raise the GitHub API limit, or wait for the hourly reset.");
} else throw err;
} Prevention
- Always set GITHUB_TOKEN/GH_TOKEN in CI and on shared IPs.
- Check /rate_limit remaining before scripted update loops.
- Back off with jitter on 403/429 instead of immediate retries.
- Keep tokens scoped (public repo read is enough for release metadata).
When it happens
Trigger: getReleaseBinaryAsset receives response.status === 429, or response.status === 403 with githubToken undefined — checked immediately after the fetch, before the generic !response.ok branch.
Common situations: Shared CI egress IPs exhausting the anonymous quota, running updates repeatedly on the same IP without a token, proxies whose exit IP is already rate-limited, or automated scripts polling the releases endpoint.
Related errors
- GitHub release ${expectedTag} has no asset list
- Timed out fetching GitHub release metadata after 30s
- GitHub API error: ${response.status}
- GitHub release ${expectedTag} has ${matches.length} assets n
- GitHub release asset ${binaryName} is not fully uploaded
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/9cc3160f22e1fc95.
Report an issue: GitHub.