{"record":{"id":"7952ccef570410f7","repo":"coleam00/Archon","slug":"checksum-mismatch-expected-expectedhash-got","errorCode":null,"errorMessage":"Checksum mismatch: expected ${expectedHash}, got ${actualHash}","messagePattern":"Checksum mismatch: expected (.+?), got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/cli/src/commands/serve.ts","lineNumber":172,"sourceCode":"    }\n    const checksumsText = await checksumsRes.text();\n    expectedHash = parseChecksum(checksumsText, 'archon-web.tar.gz');\n    log.info({ source: 'remote' }, 'web_dist.checksum_resolved');\n    tarballRes = fetchedTarballRes;\n  }\n\n  if (!tarballRes.ok) {\n    throw new Error(`Failed to download web UI: ${tarballRes.status} ${tarballRes.statusText}`);\n  }\n  const tarballBuffer = await tarballRes.arrayBuffer();\n\n  // Verify checksum\n  const hasher = new Bun.CryptoHasher('sha256');\n  hasher.update(new Uint8Array(tarballBuffer));\n  const actualHash = hasher.digest('hex');\n\n  if (actualHash !== expectedHash) {\n    throw new Error(`Checksum mismatch: expected ${expectedHash}, got ${actualHash}`);\n  }\n  console.log('Checksum verified.');\n  const verifiedAt = performance.now();\n  log.info({ durationMs: Math.round(verifiedAt - downloadStartedAt) }, 'web_dist.tarball_verified');\n\n  // Extract to temp dir, then atomic rename\n  const tmpDir = `${targetDir}.tmp`;\n  const tarballPath = `${tmpDir}.tar.gz`;\n\n  // Clean up any previous failed attempt\n  rmSync(tmpDir, { recursive: true, force: true });\n  mkdirSync(tmpDir, { recursive: true });\n\n  // Stage the archive on disk so `tar` inherits a file descriptor. Passing the\n  // bytes as `stdin` instead makes the parent own a channel it has to pump and\n  // close, and on windows that pump can stall with no upper bound: two spawns in\n  // one process sat with `tar` blocked on an unfed stdin until the test runner\n  // killed them, on a runner where the same extraction took 16 ms minutes later","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/cli/src/commands/serve.ts#L154-L190","documentation":"After downloading archon-web.tar.gz, downloadWebDist hashes the tarball with Bun.CryptoHasher (sha256) and compares it to the expected hash resolved from the published checksums.txt (parseChecksum). A mismatch means the downloaded bytes differ from the release the checksums file describes, so the tarball is rejected before extraction. This protects against truncated/corrupt downloads and compromised or mismatched artifacts.","triggerScenarios":"serveCommand -> downloadWebDist downloads the tarball, computes sha256, and it !== expectedHash parsed from checksums.txt: truncated download, interrupted connection reusing a partial buffer, CDN serving a stale/different artifact, proxy tampering, or checksums.txt and tarball fetched from mismatched versions.","commonSituations":"Flaky network truncating the body; a mirror or CDN cache serving an old tarball; downloading checksums.txt from release X but the tarball from a cached copy of release Y; corporate TLS-inspection proxy modifying content; disk cache corruption.","solutions":["Delete any cached/partial download and re-fetch — most mismatches are corrupted transfers; retry the serve command.","Verify which release the checksums.txt came from and ensure tarball and checksums come from the SAME release/version.","Check for a proxy/AV that rewrites response bodies and bypass it for the download host.","Compare the published hash manually (sha256sum on the tarball) and report upstream if the official artifact genuinely doesn't match its checksums."],"exampleFix":"// before\ntarballBuffer = await res.arrayBuffer(); // no retry on corrupt download\n// after\nlet tarballBuffer = await res.arrayBuffer();\nif (sha256(tarballBuffer) !== expectedHash) {\n  tarballBuffer = await refetchTarball(); // one retry for transient corruption\n  if (sha256(tarballBuffer) !== expectedHash) {\n    throw new Error(`Checksum mismatch: expected ${expectedHash}, got ${actualHash}`);\n  }\n}","handlingStrategy":"validation","validationCode":"import { createHash } from 'node:crypto';\nconst buf = Buffer.from(await res.arrayBuffer());\nconst actual = createHash('sha256').update(buf).digest('hex');\nif (actual !== expectedHash) {\n  throw new Error(`Pre-check: downloaded tarball hash ${actual} != expected ${expectedHash}; refetching from the same release tag is advised`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await serveCommand();\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Checksum mismatch:')) {\n    // purge any cached/partial artifact and retry exactly once;\n    // if it mismatches again, stop — do not bypass verification.\n  } else throw err;\n}","preventionTips":["Always fetch tarball and checksums.txt from the same release tag in the same script run.","Avoid proxies/AV that rewrite HTTPS bodies for the download host, or allowlist it.","Retry with a fresh connection rather than reusing cached bodies after a mismatch.","Never disable checksum verification to 'unblock' — that converts this error into a supply-chain risk."],"tags":["security","checksum","integrity","download"],"backgroundTag":"checksum-mismatch","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}