oven-sh/bun · error · Error
github ${p}: ${r.status} ${r.statusText}
Error message
github ${p}: ${r.status} ${r.statusText} What it means
The gh() helper inside compareGithubReleases() (local mode of scripts/binary-size.ts) throws when fetching releases/latest or releases/tags/canary from api.github.com returns non-2xx. status + statusText distinguish rate limiting (403) from a missing release (404).
Source
Thrown at scripts/binary-size.ts:266
function fmtBytes(n: number): string {
return `${(n / 1024 / 1024).toFixed(2)} MB`;
}
function fmtDelta(n: number): string {
const sign = n >= 0 ? "+" : "-";
const abs = Math.abs(n);
return abs >= 1024 * 1024 ? `${sign}${(abs / 1024 / 1024).toFixed(2)} MB` : `${sign}${(abs / 1024).toFixed(1)} KB`;
}
// ─── local mode: canary vs latest tagged release ───
type GithubRelease = { tag_name: string; assets: { name: string; browser_download_url: string }[] };
async function compareGithubReleases() {
const auth = process.env.GITHUB_TOKEN ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } : undefined;
const gh = (p: string) =>
fetch(`https://api.github.com/repos/oven-sh/bun/${p}`, { headers: auth }).then(r => {
if (!r.ok) throw new Error(`github ${p}: ${r.status} ${r.statusText}`);
return r.json() as Promise<GithubRelease>;
});
const [latest, canary] = await Promise.all([gh("releases/latest"), gh("releases/tags/canary")]);
// The release zips we care about are the stripped runtime binaries:
// bun-<os>-<arch>[-musl][-baseline].zip. Skip -profile (unstripped) and
// anything that isn't a single-binary zip.
const isBinaryZip = (n: string) => /^bun-[a-z0-9-]+\.zip$/.test(n) && !n.includes("-profile");
const assetMap = (r: GithubRelease) =>
new Map(r.assets.filter(a => isBinaryZip(a.name)).map(a => [a.name.replace(/\.zip$/, ""), a.browser_download_url]));
const latestAssets = assetMap(latest);
const canaryAssets = assetMap(canary);
const triplets = [...latestAssets.keys()].filter(t => canaryAssets.has(t)).sort();
process.stderr.write(`Reading ${triplets.length} zips from each of ${latest.tag_name} and canary…\n`);
const [latestSizes, canarySizes] = await Promise.all([View on GitHub (pinned to 8c5296ac45)
Solutions
- export GITHUB_TOKEN=$(gh auth token) before running
- On 404 for canary: wait for release automation to finish publishing and re-run
- On 403: wait out the rate-limit window or authenticate
Example fix
# before $ bun scripts/binary-size.ts Error: github releases/tags/canary: 404 # after $ export GITHUB_TOKEN=$(gh auth token) $ bun scripts/binary-size.ts
Defensive patterns
Strategy: retry
Validate before calling
if (!process.env.GITHUB_TOKEN) {
console.warn("no GITHUB_TOKEN — anonymous GitHub rate limit is 60/h, gh() may fail");
} Try / catch
try {
await compareGithubReleases();
} catch (e) {
if (/github releases\/.*: 404/.test(String(e?.message))) {
console.error("canary/latest release missing — publish may be in flight, retry shortly");
}
throw e;
} Prevention
- Authenticate all local runs that touch api.github.com
- Avoid racing the release pipeline when comparing canary
When it happens
Trigger: No GITHUB_TOKEN in the local environment with the anonymous 60 req/h quota exhausted (403); the canary tag or latest release temporarily absent while release automation is mid-publish (404); GitHub 5xx.
Common situations: Running bun scripts/binary-size.ts locally for a canary-vs-latest comparison without auth; running exactly while the release pipeline is publishing and tags are in flux.
Related errors
- github ${path}: ${res.status}
- HEAD ${url}: ${head.status}
- Range ${url}: ${res.status}
- page-cache eviction failed for ${path}; results would be war
- not called
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/3d83578c3c18f907.
Report an issue: GitHub.