can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: CFDATA block ${block} checksum mismatch

Error message

Invalid CAB archive: CFDATA block ${block} checksum mismatch

What it means

Thrown by CabFolder.#decode when a CFDATA block's optional 32-bit checksum is present (non-zero) but does not match the XOR checksum computed over the block header (from offset+4 to payload start) and the compressed payload. The CAB format uses this checksum to detect bit-level corruption; a mismatch means the bytes on disk differ from what the cabinet writer produced.

Source

Thrown at packages/utils/src/ar/cab.ts:162

		for (let block = 0; block < description.blockCount; block++) {
			if (position + DATA_BLOCK_SIZE + this.#dataReserveSize > bytes.byteLength) {
				throw new ArchiveError("Invalid CAB archive: truncated CFDATA header");
			}
			const compressed = readUInt16LE(bytes, position + 4);
			const uncompressed = readUInt16LE(bytes, position + 6);
			if (uncompressed === 0) throw new ArchiveError("Unsupported multi-volume CAB archive: split CFDATA block");
			if (uncompressed > MAX_DATA_OUTPUT) {
				throw new ArchiveError(`Invalid CAB archive: CFDATA expands to ${uncompressed} bytes (maximum 32768)`);
			}
			const payloadStart = position + DATA_BLOCK_SIZE + this.#dataReserveSize;
			const payloadEnd = payloadStart + compressed;
			if (payloadEnd > bytes.byteLength) throw new ArchiveError("Invalid CAB archive: truncated CFDATA payload");
			const expectedChecksum = readUInt32LE(bytes, position);
			if (expectedChecksum !== 0) {
				const payloadChecksum = cabChecksum(bytes.subarray(payloadStart, payloadEnd));
				const actualChecksum = cabChecksum(bytes.subarray(position + 4, payloadStart), payloadChecksum);
				if (actualChecksum !== expectedChecksum) {
					throw new ArchiveError(`Invalid CAB archive: CFDATA block ${block} checksum mismatch`);
				}
			}
			outputSize += uncompressed;
			assertInMemorySize(outputSize, this.#limits);
			position = payloadEnd;
		}
		if (outputSize < description.requiredSize) {
			throw new ArchiveError("Invalid CAB archive: folder data is shorter than its file table declares");
		}

		const output = new Uint8Array(outputSize);
		const lzx = description.method === 3 ? new LzxDecoder(description.parameter) : undefined;
		position = 0;
		let outputPosition = 0;
		for (let block = 0; block < description.blockCount; block++) {
			const compressed = readUInt16LE(bytes, position + 4);
			const uncompressed = readUInt16LE(bytes, position + 6);
			const payloadStart = position + DATA_BLOCK_SIZE + this.#dataReserveSize;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-obtain the archive from its original source and compare checksums
  2. Test with `cabextract -t file.cab` to confirm corruption is in the file, not the reader
  3. If you produce cabinets yourself, verify your writer computes the XOR checksum per the CAB spec (or stores 0 to skip checking)
  4. If you must salvage data, use a repair-oriented extractor that skips bad blocks, accepting partial output

Example fix

// before
await extractCab(untrustedPath, dest);
// after
const ok = await Bun.$`cabextract -t ${untrustedPath}`.quiet().nothrow();
if (!ok.exitCode) throw new Error(`archive corrupt, refusing extraction: ${untrustedPath}`);
await extractCab(untrustedPath, dest);
Defensive patterns

Strategy: validation

Validate before calling

const bytes = new Uint8Array(await Bun.file(path).arrayBuffer());
if (!sniffCab(bytes.subarray(0, 4))) throw new Error('not a CAB file');
// content integrity can only be checked by the reader; pre-verify whole-file hash instead
if (await sha256(path) !== expectedHash) throw new Error('file hash mismatch — corrupted or modified');

Try / catch

try {
  await readCab(source, opts);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('checksum mismatch'))
    logger.error('CAB block checksum failed; archive is corrupt', { path: archivePath });
  throw err;
}

Prevention

When it happens

Trigger: readCab() indexed a cabinet whose stored cfchecksum at the block start differs from cabChecksum(bytes[position+4..payloadStart], cabChecksum(payload)); any single-byte flip in header or payload of a checksummed block triggers it.

Common situations: Bit rot on old storage media; archives corrupted during transfer or by a faulty disk; files modified after creation; DOS/Windows-era cabinets with pre-existing damage.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/9e7de185d58ad864. Report an issue: GitHub.