Hmbown/CodeWhale · critical · Error

${manifestName} checksum mismatch for ${name}

Error message

${manifestName} checksum mismatch for ${name}

What it means

assertManifest() recomputed the sha256 of a file inside the asset directory and it differs from the digest recorded in CHECKSUM_MANIFEST or BUNDLE_CHECKSUM_MANIFEST. This is an integrity stop: the bytes on disk are not the bytes the manifest attests, so the release cannot ship. The manifest name and offending file are included in the message.

Source

Thrown at scripts/release/assemble-release-assets.js:76

      `${label} does not match the authoritative inventory` +
        `${missing.length > 0 ? `; missing: ${missing.join(", ")}` : ""}` +
        `${unexpected.length > 0 ? `; unexpected: ${unexpected.join(", ")}` : ""}` +
        `${actual.size !== actualNames.length ? "; duplicate basenames are present" : ""}`,
    );
  }
}

async function assertManifest(directory, manifestName, expectedNames) {
  const manifestPath = path.join(directory, manifestName);
  const checksums = parseChecksumManifest(
    await fs.readFile(manifestPath, "utf8"),
    manifestName,
  );
  assertExactNames([...checksums.keys()], expectedNames, manifestName);
  for (const name of expectedNames) {
    const actual = await sha256(path.join(directory, name));
    if (checksums.get(name) !== actual) {
      throw new Error(`${manifestName} checksum mismatch for ${name}`);
    }
  }
}

async function verifyAssetDirectory(directory) {
  const entries = await fs.readdir(directory, { withFileTypes: true });
  const nonFiles = entries.filter((entry) => !entry.isFile());
  if (nonFiles.length > 0) {
    throw new Error(
      `Release asset directory must be flat; found: ${nonFiles.map((entry) => entry.name).join(", ")}`,
    );
  }

  const expected = allReleaseAssetNames();
  assertExactNames(entries.map((entry) => entry.name), expected, "Release asset directory");
  await assertManifest(directory, CHECKSUM_MANIFEST, checksummedReleaseAssetNames());
  await assertManifest(directory, BUNDLE_CHECKSUM_MANIFEST, BUNDLE_ASSET_NAMES);
  console.log(`Verified ${expected.length} release assets in ${directory}`);

View on GitHub (pinned to 8880682c63)

Solutions

  1. Re-run assemble from the original artifacts so the manifest is regenerated over the exact shipped bytes — never hand-patch the manifest to match modified files
  2. Confirm which side changed: sha256sum <file> versus the manifest row quoted in the message
  3. If input artifacts may be corrupted, re-download them from the workflow run and re-assemble
  4. Ensure nothing touches the output directory between assembly and verification (no editors, formatters, or sync agents)

Example fix

# before: assembly output was later touched by a formatter -> checksum mismatch for codewhale.bat

# after: always assemble into a fresh directory and verify immediately
rm -rf release-out
node scripts/release/assemble-release-assets.js downloaded-artifacts release-out
node scripts/release/assemble-release-assets.js --verify release-out
Defensive patterns

Strategy: validation

Validate before calling

const { verifyAssetDirectory } = require("./scripts/release/assemble-release-assets");
// immediately before upload, in the same job that owns the directory:
await verifyAssetDirectory(outDir);

Prevention

When it happens

Trigger: A file modified after the manifest was written (editor or git normalizing line endings, a re-packaged archive, a partial write); a corrupted CI artifact download; a manifest carried over from a previous build while binaries are from the current one; disk-level corruption.

Common situations: CRLF normalization touching codewhale.bat or text assets between assembly and verification; mixing manifests and binaries from different CI attempts; flaky network truncating an artifact during download.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/8fc32d306f4af53f. Report an issue: GitHub.