JuliusBrussee/caveman · error · Error
signature check failed for
Error message
signature check failed for ${artifact} — partial download deleted What it means
After streaming the artifact into a uniquely named .part file, `download()` returns the sha256 of what was written; `ensureBinary` compares it to the digest from the signed manifest. On mismatch it throws this error and the catch block deletes the .part file, so a corrupted or truncated download can never be promoted to the install target.
Solutions
- Simply retry the install — the .part file is deleted and the next attempt uses a fresh unique name, so retrying is safe.
- Check network stability (wired connection, disable VPN/proxy) if retries keep failing.
- Verify the server-side artifact actually matches checksums.txt: `sha256sum <artifact>` after a manual download.
- If the server artifact is genuinely wrong, report the release and use an earlier working release, or install the binary manually via the envVar override.
Example fix
// before: repeated corrupt download over flaky network
await ensureBinary({ name: "caveman-mcp", envVar: "CAVEMAN_MCP_BIN" });
// after: retry with backoff
for (let i = 0; i < 3; i++) {
try { await ensureBinary({ name: "caveman-mcp", envVar: "CAVEMAN_MCP_BIN" }); break; }
catch (e) { if (i === 2) throw e; await new Promise(r => setTimeout(r, 2 ** i * 1000)); }
} Defensive patterns
Strategy: retry
Try / catch
async function installWithRetry(opts, attempts = 3) {
for (let i = 0; i < attempts; i++) {
try { return await ensureBinary(opts); }
catch (e) {
if (!String(e.message).includes("partial download deleted") || i === attempts - 1) throw e;
await new Promise(r => setTimeout(r, 2 ** i * 500));
}
}
} Prevention
- Ensure stable connectivity for large downloads; avoid flaky VPNs during install.
- Retries are safe: each attempt uses a fresh unique .part name and deletes failed parts.
- Verify server-side artifacts match checksums.txt after release publication.
- Monitor CDN for stale object caching after releases.
When it happens
Trigger: Calling `ensureBinary()` where `download(release/artifact, part, timeout)` returns a hash differing from `expected` — truncated download (connection dropped mid-stream), CDN serving a stale/partial artifact, disk corruption, or an artifact replaced on the server after checksums.txt was signed.
Common situations: Flaky hotel/VPN networks cutting large downloads short; CDN edge serving an old artifact revision; interrupted resume logic in a middlebox; concurrent download tools corrupting disk sectors (rare).
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- ccr: typed object content_hash does not match data
- signature check failed for
- adapter-specific build_sha256 values must differ
- binary download failed
- binary download failed: HTTP
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/e0974b2c6619f649.
Report an issue: GitHub.
Appendix: source
Thrown at packages/shared/binary-installer/installer.mjs:217
}
const expected = expectedDigest(checksums, artifact);
mkdirSync(binDir, { recursive: true });
// Per-process part path, and NO pre-unlink. A fixed `${target}.part` plus a
// cleanup() immediately before an O_CREAT|O_EXCL open made the exclusivity
// guard unreachable: two concurrent installs (npx -y caveman-mcp is registered
// per MCP session, so several agents can start one at once) raced — B unlinked
// A's directory entry and created its own inode, A hashed ITS OWN bytes and
// matched, and then the PATH-based chmod+rename published B's half-written file
// as "checksum verified". Both the chmod and the rename now act on a name only
// this process can own.
//
// ponytail: a SIGKILL mid-download now leaves one stray .part behind instead of
// reusing the fixed name. Sweep binDir for stale .part files if that ever shows
// up in the wild.
const part = `${target}.${process.pid}.${randomBytes(6).toString("hex")}.part`;
try {
const actual = await download(`${release}/${artifact}`, part, timeout);
if (actual !== expected) throw new Error(`signature check failed for ${artifact} — partial download deleted`);
chmodSync(part, 0o755);
await replaceWithRetry(part, target);
} catch (error) {
cleanup(part);
throw error;
}
process.stderr.write(`${name} ${os}/${arch} checksum verified\n`);
return target;
}
View on GitHub (pinned to 3ee70a1026)