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

  1. Simply retry the install — the .part file is deleted and the next attempt uses a fresh unique name, so retrying is safe.
  2. Check network stability (wired connection, disable VPN/proxy) if retries keep failing.
  3. Verify the server-side artifact actually matches checksums.txt: `sha256sum <artifact>` after a manual download.
  4. 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

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


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)