JuliusBrussee/caveman · error

duplicate checksum manifest entry

Error message

duplicate checksum manifest entry: ${filename}

What it means

parseSignedChecksums builds a Map keyed by filename and throws if the same filename appears twice in the manifest. Duplicate entries make the signature verification ambiguous, so the whole manifest is rejected.

Solutions

  1. Regenerate the manifest in one pass (sha256sum * > checksums.txt) instead of appending.
  2. Deduplicate with `awk '!seen[$2]++' checksums.txt` after confirming entries agree.
  3. Diff the two merged manifests and keep only the current release's entries.

Example fix

// before (appended twice)
abc...  cave-darwin-arm64
def...  cave-darwin-arm64

// after
abc...  cave-darwin-arm64
Defensive patterns

Strategy: validation

Validate before calling

const names = raw.split('\n').filter(Boolean).map(l => l.match(/^[a-f0-9]{64}  ([A-Za-z0-9._-]+)$/)?.[1]);
const dupes = names.filter((n, i) => n && names.indexOf(n) !== i);
if (dupes.length) throw new Error(`duplicate manifest entries: ${[...new Set(dupes)].join(', ')}`);

Try / catch

try {
  verify(manifest);
} catch (error) {
  if (error.message.startsWith('duplicate checksum manifest entry')) {
    console.error(`${error.message}\nRegenerate the manifest in a single pass instead of appending.`);
  } else throw error;
}

Prevention

When it happens

Trigger: A checksum manifest generated by concatenating two manifest files (or running sha256sum twice with append), producing the same filename on two lines.

Common situations: CI scripts doing `sha256sum * >> checksums.txt` on retries; merging an updated manifest into an old one without deduplication.

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/c74d41082f708ae3. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:2279

  if (!manifest || manifest.release !== BINARY_RELEASE) return null;
  const installed: InstalledBinary[] = [];
  for (const name of INSTALL_BINARIES) {
    const expected = manifest.artifacts[name];
    const path = join(binDir, binaryInstallFilename(name));
    if (!expected || sha256File(path) !== expected) return null;
    installed.push({ name, path, sha256: expected, status: "already installed" });
  }
  return installed;
}

function parseSignedChecksums(raw: string): Map<string, string> {
  const checksums = new Map<string, string>();
  for (const line of raw.split("\n")) {
    if (!line) continue;
    const match = line.match(/^([a-f0-9]{64})  ([A-Za-z0-9._-]+)$/);
    if (!match) throw new Error(`invalid checksum manifest line: ${JSON.stringify(line)}`);
    const filename = match[2]!;
    if (checksums.has(filename)) throw new Error(`duplicate checksum manifest entry: ${filename}`);
    checksums.set(filename, match[1]!);
  }
  return checksums;
}

function verifyChecksumSignature(checksums: string, signature: string): boolean {
  try {
    const bundle = JSON.parse(signature) as {
      mediaType?: unknown;
      messageSignature?: {
        messageDigest?: { algorithm?: unknown; digest?: unknown };
        signature?: unknown;
      };
    };
    if (bundle.mediaType !== "application/vnd.dev.sigstore.bundle.v0.3+json") return false;
    if (bundle.messageSignature?.messageDigest?.algorithm !== "SHA2_256") return false;
    if (typeof bundle.messageSignature.messageDigest.digest !== "string") return false;
    if (typeof bundle.messageSignature.signature !== "string") return false;

View on GitHub (pinned to 3ee70a1026)