{"record":{"id":"02c29f456c570d71","repo":"JuliusBrussee/caveman","slug":"binary-download-failed-response-body-missing","errorCode":null,"errorMessage":"binary download failed: response body missing","messagePattern":"binary download failed: response body missing","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shared/binary-installer/installer.mjs","lineNumber":137,"sourceCode":"    if (!line) continue;\n    const match = line.match(/^([a-f0-9]{64})  ([A-Za-z0-9._-]+)$/);\n    if (!match) throw new Error(\"signed checksum manifest is malformed\");\n    if (match[2] === artifact) return match[1];\n  }\n  throw new Error(`signed checksum manifest does not contain ${artifact}`);\n}\n\nfunction cleanup(path) {\n  try {\n    unlinkSync(path);\n  } catch (error) {\n    if (error.code !== \"ENOENT\") throw error;\n  }\n}\n\nasync function download(url, part, timeout) {\n  const response = await asset(url, timeout);\n  if (!response.body) throw new Error(\"binary download failed: response body missing\");\n  const hash = createHash(\"sha256\");\n  const file = await open(part, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);\n  const reader = response.body.getReader();\n  try {\n    while (true) {\n      const { done, value } = await reader.read();\n      if (done) break;\n      hash.update(value);\n      await file.write(value);\n    }\n  } finally {\n    reader.releaseLock();\n    await file.close();\n  }\n  return hash.digest(\"hex\");\n}\n\nexport async function ensureBinary({ name, envVar }) {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/shared/binary-installer/installer.mjs#L119-L155","documentation":"download() in the binary installer requires response.body (a web ReadableStream) before streaming the artifact to disk and hashing it. A response that is ok yet has a falsy body cannot be written or verified, so it fails before creating the .part file.","triggerScenarios":"Interceptors or test doubles returning Response-like objects without a body; fetch implementations (older polyfills, some edge runtimes) that expose arrayBuffer() but a null body; a proxy returning an empty 200.","commonSituations":"Running the installer under a custom global fetch shim; unit tests mocking fetch with literal { ok: true } objects; unusual runtimes where Node's undici has been replaced.","solutions":["Retry once — genuinely transient empty 200 responses exist behind flaky proxies","If a custom fetch or interceptor is installed globally, disable it for the install step or make it return real Response objects","Verify with curl -i that the artifact URL really returns a body; if not, treat it as a server or mirror problem (HTTP-status failure)"],"exampleFix":"# before (custom global fetch mock in the process)\nglobalThis.fetch = async () => ({ ok: true });   // no body → error\n\n# after (during install)\nglobalThis.fetch = undici.fetch;   # or drop the shim before running setup","handlingStrategy":"retry","validationCode":"// guard if you inject a custom fetch into the installer's process\nif (typeof globalThis.fetch === \"function\") {\n  const probe = await globalThis.fetch(\"data:text/plain,1\");\n  if (!probe.body && typeof probe.arrayBuffer !== \"function\") {\n    throw new Error(\"fetch shim lacks body — disable it for setup\");\n  }\n}","typeGuard":"function hasBody(response) {\n  return response.body != null && typeof response.body.getReader === \"function\";\n}","tryCatchPattern":"try { await ensureBinary({ name, envVar }); }\ncatch (e) {\n  if (/response body missing/.test(String(e?.message))) {\n    await delay(1000);\n    return ensureBinary({ name, envVar });\n  }\n  throw e;\n}","preventionTips":["Do not run the installer in processes with mocked or partial global fetch shims","Disable test fetch mocks around setup steps"],"tags":["installer","fetch","network"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}