JuliusBrussee/caveman · error

binary download failed: HTTP ${response.status}

Error message

binary download failed: HTTP ${response.status}

What it means

asset() in the binary installer got an HTTP response but response.ok was false, so it fails with the status code. Unlike the transport-level throw, this means the server answered and rejected the request — the URL was reached, but the response was not acceptable.

Source

Thrown at packages/shared/binary-installer/installer.mjs:94

}

function timeoutMs() {
  const raw = process.env.CAVE_SETUP_TIMEOUT ?? "300";
  const seconds = Number(raw);
  if (!Number.isInteger(seconds) || seconds <= 0) {
    throw new Error(`CAVE_SETUP_TIMEOUT must be a positive integer (got ${JSON.stringify(raw)})`);
  }
  return seconds * 1000;
}

async function asset(url, timeout) {
  let response;
  try {
    response = await fetch(url, { signal: AbortSignal.timeout(timeout) });
  } catch (error) {
    throw new Error(`binary download failed: ${error.message}`);
  }
  if (!response.ok) throw new Error(`binary download failed: HTTP ${response.status}`);
  return response;
}

function signedDigest(checksums, bundleRaw) {
  try {
    const bundle = JSON.parse(bundleRaw);
    if (bundle.mediaType !== "application/vnd.dev.sigstore.bundle.v0.3+json") return false;
    if (bundle.messageSignature?.messageDigest?.algorithm !== "SHA2_256") return false;
    const digest = createHash("sha256").update(checksums).digest();
    const bundled = Buffer.from(bundle.messageSignature.messageDigest.digest, "base64");
    if (digest.length !== bundled.length || !digest.equals(bundled)) return false;
    return verify(
      "sha256",
      Buffer.from(checksums),
      createPublicKey(BINARY_SIGNING_PUBKEY),
      Buffer.from(bundle.messageSignature.signature, "base64"),
    );
  } catch {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. curl the exact release URL (base + tag + checksums.txt) to see the status directly
  2. If using a custom CAVE_BINARY_RELEASE_BASE, verify the release tag and file layout exist there; otherwise unset it to use the default host
  3. For GitHub-hosted assets: authenticate or wait out rate limits on 403; retry on 5xx after the release finishes publishing

Example fix

# before
CAVE_BINARY_RELEASE_BASE=https://mirror.internal/nope/rels …  # 404

# after
unset CAVE_BINARY_RELEASE_BASE   # use default
# or fix the mirror: CAVE_BINARY_RELEASE_BASE=https://mirror.internal/rels/2026-08-01
Defensive patterns

Strategy: retry

Validate before calling

const base = process.env.CAVE_BINARY_RELEASE_BASE ?? DEFAULT;
const head = await fetch(`${base}/${tag}/checksums.txt`, { method: "HEAD" });
if (!head.ok) throw new Error(`release unavailable (HTTP ${head.status}) — fix base/tag before setup`);

Try / catch

try { await ensureBinary({ name, envVar }); }
catch (e) {
  const m = /binary download failed: HTTP (\d+)/.exec(String(e?.message));
  if (!m) throw e;
  const status = Number(m[1]);
  if (status === 404) throw new Error("release asset missing — check CAVE_BINARY_RELEASE_BASE/tag");
  if (status === 403 || status >= 500) await backoffRetry(); // rate limit or transient
  else throw e;
}

Prevention

When it happens

Trigger: 404 when CAVE_BINARY_RELEASE_BASE or the pinned BINARY_RELEASE tag points at a release whose checksums.txt or assets were not published; 403 from rate limiting or a private release; 502/503 during a host outage; a wrong custom mirror path.

Common situations: Overriding CAVE_BINARY_RELEASE_BASE to an internal mirror missing the tag; running during a GitHub release publication window; rate-limited anonymous GitHub access in CI; a typo'd base URL.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/4b4ae45739663024. Report an issue: GitHub.