JuliusBrussee/caveman · error · Error
binary download failed: response body missing
Error message
binary download failed: response body missing
What it means
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.
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");
}
export async function ensureBinary({ name, envVar }) {View on GitHub (pinned to 27d5a3981a)
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)
Example fix
# before (custom global fetch mock in the process)
globalThis.fetch = async () => ({ ok: true }); // no body → error
# after (during install)
globalThis.fetch = undici.fetch; # or drop the shim before running setup Defensive patterns
Strategy: retry
Validate before calling
// guard if you inject a custom fetch into the installer's process
if (typeof globalThis.fetch === "function") {
const probe = await globalThis.fetch("data:text/plain,1");
if (!probe.body && typeof probe.arrayBuffer !== "function") {
throw new Error("fetch shim lacks body — disable it for setup");
}
} Type guard
function hasBody(response) {
return response.body != null && typeof response.body.getReader === "function";
} Try / catch
try { await ensureBinary({ name, envVar }); }
catch (e) {
if (/response body missing/.test(String(e?.message))) {
await delay(1000);
return ensureBinary({ name, envVar });
}
throw e;
} Prevention
- Do not run the installer in processes with mocked or partial global fetch shims
- Disable test fetch mocks around setup steps
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- binary download failed: ${error.message}
- binary download failed: HTTP ${response.status}
- signature check failed for ${artifact} — partial download de
- registration failed: HTTP ${response.status}
- cave_sandbox_os_network_isolation_unavailable
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/02c29f456c570d71.
Report an issue: GitHub.