can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: truncated CFDATA payload

Error message

Invalid CAB archive: truncated CFDATA payload

What it means

Thrown by CabFolder.#decode when a CFDATA block's declared compressed size extends past the end of the folder's byte range read from the archive. The library validates every data block boundary before decompressing, so a cabinet whose payload region is shorter than the block table claims is rejected instead of producing a partial or garbage extraction.

Source

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

		const compressedSize = description.dataEnd - description.dataStart;
		assertInMemorySize(compressedSize, this.#limits);
		const bytes = await readExact(this.#source, description.dataStart, description.dataEnd);
		let position = 0;
		let outputSize = 0;
		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;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download or re-copy the .cab archive and verify its size/hash against the source
  2. Verify the archive opens with an independent tool (e.g. `cabextract -t file.cab`) to confirm the file is corrupt, not the library
  3. If the file comes from your own build pipeline, check the step that produces the cabinet for premature stream close
  4. Wrap extraction in try-catch on ArchiveError and surface a 'corrupt archive, re-fetch' message to users

Example fix

// before
const data = await Bun.file(downloadPath).arrayBuffer();
await readCab(new Uint8Array(data), opts);
// after
const data = await Bun.file(downloadPath).arrayBuffer();
const expected = await fetchExpectedSize();
if (data.byteLength !== expected) throw new Error(`incomplete download: ${data.byteLength}/${expected}`);
await readCab(new Uint8Array(data), opts);
Defensive patterns

Strategy: validation

Validate before calling

const stat = await Bun.file(path).stat();
const expectedSize = await getExpectedSize(path); // from manifest/CDN
if (stat.size !== expectedSize) throw new Error(`truncated archive: ${stat.size} != ${expectedSize}`);

Try / catch

try {
  await readCab(source, opts);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('truncated CFDATA payload'))
    throw new Error('Archive is incomplete or corrupt — re-download it.');
  throw err;
}

Prevention

When it happens

Trigger: readCab() indexed a cabinet whose CFFOLDER declared a block whose cbData (compressed size at offset+4) makes payloadEnd exceed the folder's data region; typically the file was truncated mid-download or the folder data ranges were corrupted.

Common situations: Interrupted downloads or uploads of .cab files; partial Git LFS/CDN fetches; archives corrupted in transit; hand-crafted or fuzzed cabinet files; extracting from a network stream that was cut short.

Related errors


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