JuliusBrussee/caveman · error · Error

binary download failed: response body missing

Error message

binary download failed: response body missing

What it means

`download(url, part, timeout)` first obtains a response via `asset()` (already status-checked), then requires a streaming body to hash and write to the .part file. If `response.body` is null/undefined, the response cannot be streamed, so it throws immediately before opening the temp file. Defensive-undeclared-stream guard against runtimes/proxies that swallow the body.

Solutions

  1. Verify you're on a Node version with a real undici-based fetch (Node >= 18) so responses carry a ReadableStream body.
  2. Check whether global fetch is mocked in your process/test setup and restore the real fetch for the installer.
  3. curl the artifact URL to confirm the server streams bytes; if a proxy strips bodies, bypass it.
  4. Install the binary out-of-band and set the envVar so download() is never invoked.

Example fix

// before: mocked fetch without body
global.fetch = async () => ({ ok: true, status: 200, text: async () => "" });
// after: provide a body stream
global.fetch = async () => ({ ok: true, status: 200, body: new Blob([data]).stream() });
Defensive patterns

Strategy: type-guard

Type guard

function hasBody(res) { return res != null && typeof res.body?.getReader === "function"; }

Try / catch

try {
  await ensureBinary({ name, envVar });
} catch (e) {
  if (String(e.message).includes("response body missing")) {
    console.error("fetch response had no stream — check fetch mocks / runtime");
  }
  throw e;
}

Prevention

When it happens

Trigger: `download()` called (from `ensureBinary`) where the fetch response from `asset()` has a null body — e.g. a stubbed/mocked global fetch in tests, an HTTP/0.9 or empty-bodied 204-style response from a misbehaving middlebox, or an undici version where body is consumed/null for certain responses.

Common situations: Test environments where fetch is mocked without a `body` ReadableStream; corporate middleboxes that terminate streaming responses; exotic runtimes (older Node with different fetch polyfill) lacking a spec-compliant body stream.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/02c29f456c570d71. Report an issue: GitHub.

Appendix: source

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

    if (!line) continue;
    const match = line.match(/^([a-f0-9]{64})  ([A-Za-z0-9._-]+)$/);
    if (!match) throw new Error("signed checksum manifest is malformed");
    if (match[2] === artifact) return match[1];
  }
  throw new Error(`signed checksum manifest does not contain ${artifact}`);
}

function cleanup(path) {
  try {
    unlinkSync(path);
  } catch (error) {
    if (error.code !== "ENOENT") throw error;
  }
}

async function download(url, part, timeout) {
  const response = await asset(url, timeout);
  if (!response.body) throw new Error("binary download failed: response body missing");
  const hash = createHash("sha256");
  const file = await open(part, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);
  const reader = response.body.getReader();
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      hash.update(value);
      await file.write(value);
    }
  } finally {
    reader.releaseLock();
    await file.close();
  }
  return hash.digest("hex");
}

// Windows refuses to replace a file another process holds open, and the

View on GitHub (pinned to 3ee70a1026)