JuliusBrussee/caveman · error · Error
binary download failed
Error message
binary download failed: ${error.message} What it means
`asset` wraps the installer's fetch of a binary/checksum URL. Any network- or abort-level failure of fetch (DNS failure, connection refused/reset, TLS error, or the AbortSignal.timeout firing) is rethrown as `binary download failed: <original message>`, preserving the cause's message.
Solutions
- Check basic egress first: curl -I the URL from the same machine to see the underlying cause
- Configure HTTPS_PROXY/HTTP_PROXY if the environment requires a proxy for outbound fetch
- Raise CAVE_SETUP_TIMEOUT so AbortSignal.timeout doesn't abort slow downloads
- Retry the install (transient network/DNS failures) or pre-download the binary and install it manually
Example fix
// before // installer aborts on slow link: binary download failed: The operation was aborted due to timeout // after CAVE_SETUP_TIMEOUT=900 npx installer
Defensive patterns
Strategy: retry
Validate before calling
// probe egress before install
const probe = await fetch("https://github.com", { method: "HEAD", signal: AbortSignal.timeout(5000) }).catch(() => null);
if (!probe) throw new Error("no outbound network access; configure proxy or vendor the binary"); Try / catch
async function withRetry(fn, n = 3) {
let last;
for (let i = 0; i < n; i++) {
try { return await fn(); }
catch (e) {
if (!String(e.message).startsWith("binary download failed:")) throw e;
last = e;
await new Promise(r => setTimeout(r, 2 ** i * 1000));
}
}
throw last;
} Prevention
- Set HTTPS_PROXY/HTTP_PROXY where egress requires a proxy
- Raise CAVE_SETUP_TIMEOUT for slow links
- Pre-download binaries and cache them in CI between runs
- Retry transient failures with backoff; surface the wrapped cause message for diagnosis
When it happens
Trigger: fetch rejects: no network/DNS, unreachable or firewalled host, proxy issues, TLS interception, or the request exceeding the timeout passed to asset (AbortSignal.timeout aborts).
Common situations: Corporate proxy blocking the download host; GitHub Releases rate-limit or outage; air-gapped CI with no egress; slow link where the setup timeout (CAVE_SETUP_TIMEOUT) expires mid-download.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- AbortError
- binary download failed: HTTP
- binary download failed: response body missing
- bounded_response_unavailable
- \nfix
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/aecd62c308c613e4.
Report an issue: GitHub.
Appendix: source
Thrown at packages/shared/binary-installer/installer.mjs:92
export function binaryInstallFilename(name, os = process.platform) {
return os === "win32" ? `${name}.exe` : name;
}
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"),View on GitHub (pinned to 3ee70a1026)