{"record":{"id":"4b4ae45739663024","repo":"JuliusBrussee/caveman","slug":"binary-download-failed-http-response-status","errorCode":null,"errorMessage":"binary download failed: HTTP ${response.status}","messagePattern":"binary download failed: HTTP (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/shared/binary-installer/installer.mjs","lineNumber":94,"sourceCode":"}\n\nfunction timeoutMs() {\n  const raw = process.env.CAVE_SETUP_TIMEOUT ?? \"300\";\n  const seconds = Number(raw);\n  if (!Number.isInteger(seconds) || seconds <= 0) {\n    throw new Error(`CAVE_SETUP_TIMEOUT must be a positive integer (got ${JSON.stringify(raw)})`);\n  }\n  return seconds * 1000;\n}\n\nasync function asset(url, timeout) {\n  let response;\n  try {\n    response = await fetch(url, { signal: AbortSignal.timeout(timeout) });\n  } catch (error) {\n    throw new Error(`binary download failed: ${error.message}`);\n  }\n  if (!response.ok) throw new Error(`binary download failed: HTTP ${response.status}`);\n  return response;\n}\n\nfunction signedDigest(checksums, bundleRaw) {\n  try {\n    const bundle = JSON.parse(bundleRaw);\n    if (bundle.mediaType !== \"application/vnd.dev.sigstore.bundle.v0.3+json\") return false;\n    if (bundle.messageSignature?.messageDigest?.algorithm !== \"SHA2_256\") return false;\n    const digest = createHash(\"sha256\").update(checksums).digest();\n    const bundled = Buffer.from(bundle.messageSignature.messageDigest.digest, \"base64\");\n    if (digest.length !== bundled.length || !digest.equals(bundled)) return false;\n    return verify(\n      \"sha256\",\n      Buffer.from(checksums),\n      createPublicKey(BINARY_SIGNING_PUBKEY),\n      Buffer.from(bundle.messageSignature.signature, \"base64\"),\n    );\n  } catch {","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/shared/binary-installer/installer.mjs#L76-L112","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["curl the exact release URL (base + tag + checksums.txt) to see the status directly","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","For GitHub-hosted assets: authenticate or wait out rate limits on 403; retry on 5xx after the release finishes publishing"],"exampleFix":"# before\nCAVE_BINARY_RELEASE_BASE=https://mirror.internal/nope/rels …  # 404\n\n# after\nunset CAVE_BINARY_RELEASE_BASE   # use default\n# or fix the mirror: CAVE_BINARY_RELEASE_BASE=https://mirror.internal/rels/2026-08-01","handlingStrategy":"retry","validationCode":"const base = process.env.CAVE_BINARY_RELEASE_BASE ?? DEFAULT;\nconst head = await fetch(`${base}/${tag}/checksums.txt`, { method: \"HEAD\" });\nif (!head.ok) throw new Error(`release unavailable (HTTP ${head.status}) — fix base/tag before setup`);","typeGuard":null,"tryCatchPattern":"try { await ensureBinary({ name, envVar }); }\ncatch (e) {\n  const m = /binary download failed: HTTP (\\d+)/.exec(String(e?.message));\n  if (!m) throw e;\n  const status = Number(m[1]);\n  if (status === 404) throw new Error(\"release asset missing — check CAVE_BINARY_RELEASE_BASE/tag\");\n  if (status === 403 || status >= 500) await backoffRetry(); // rate limit or transient\n  else throw e;\n}","preventionTips":["curl-check a custom mirror's base and tag layout before pointing CAVE_BINARY_RELEASE_BASE at it","Treat 404 as permanent (config) and 403/5xx as transient (retry or rate-limit)"],"tags":["http","network","installer"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}