schollz/croc · error

GitHub release request failed (${response.status})

Error message

GitHub release request failed (${response.status})

What it means

Thrown by fetchLatestRelease() when the GitHub API request for the latest release completes with a non-ok HTTP status; the status code is embedded in the message. It distinguishes transport success from API failure before the response body is parsed as a release.

Source

Thrown at web/src/releases.ts:130

  const preferredArchitecture =
    architecture === "unknown"
      ? platform === "macOS"
        ? "arm64"
        : "x64"
      : architecture;
  return (
    assets.find((asset) => assetArchitecture(asset) === preferredArchitecture) ??
    assets[0]
  );
}

export async function fetchLatestRelease(signal?: AbortSignal) {
  const response = await fetch(latestReleaseAPI, {
    signal,
    headers: { Accept: "application/vnd.github+json" },
  });
  if (!response.ok) {
    throw new Error(`GitHub release request failed (${response.status})`);
  }
  const release = (await response.json()) as GitHubRelease;
  if (
    !release.tag_name ||
    !release.html_url ||
    !Array.isArray(release.assets)
  ) {
    throw new Error("GitHub returned invalid release metadata");
  }
  return release;
}

View on GitHub (pinned to e25f1bdc04)

Solutions

  1. On 403, back off respecting X-RateLimit-Reset or add an Authorization header / GITHUB_TOKEN to raise the limit
  2. On 404, verify the repository and that a published (non-draft) release exists
  3. Cache the last successful release response and serve stale on failure
  4. Retry with exponential backoff for transient 5xx

Example fix

// before
const release = await fetchLatestRelease(signal);

// after
let release;
try {
  release = await fetchLatestRelease(signal);
} catch (e) {
  if (e instanceof Error && e.message.includes('(403)')) release = lastGoodRelease;
  else throw e;
}
Defensive patterns

Strategy: retry

Validate before calling

async function releaseEndpointHealthy(signal?: AbortSignal): Promise<boolean> {
  const r = await fetch(latestReleaseAPI, { method: 'HEAD', signal });
  return r.ok;
}

Try / catch

try { return await fetchLatestRelease(signal); } catch (e) { if (e instanceof Error && /\(403\)/.test(e.message)) return lastGoodRelease; if (e instanceof Error && /\(5\d\d\)/.test(e.message)) return retryWithBackoff(() => fetchLatestRelease(signal), 3); throw e; }

Prevention

When it happens

Trigger: Calling fetchLatestRelease() when api.github.com returns 403 (unauthenticated rate limit of 60 req/hr/IP exhausted), 404 (repo or release missing), or a 5xx.

Common situations: CI pipelines polling the endpoint frequently; many users behind one NAT/proxy egress IP; GitHub incidents; repository renamed so the API 404s.

Related errors


AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15). Data as JSON: /api/errors/976243a6824ae936. Report an issue: GitHub.