can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: folder data is shorter than its file ta

Error message

Invalid CAB archive: folder data is shorter than its file table declares

What it means

Thrown by CabFolder.#decode after summing every block's declared uncompressed size: if the total is less than the maximum (offset+size) any CFFILE entry in this folder requires, the folder cannot supply the bytes its file table promises. The library cross-checks the folder data against the file table instead of returning short reads.

Source

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

				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;
			const payloadEnd = payloadStart + compressed;
			const payload = bytes.subarray(payloadStart, payloadEnd);
			let decoded: Uint8Array;
			if (description.method === 0) {
				if (compressed !== uncompressed) {
					throw new ArchiveError("Invalid CAB archive: uncompressed CFDATA sizes do not match");
				}
				decoded = payload;

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the archive with `cabextract -t` or 7-Zip; if they also fail, the file is malformed
  2. Regenerate the cabinet with a standard tool (makecab, 7-Zip, libmspack) if you control its production
  3. Check whether the cabinet passed through a process that could rewrite bytes (text-mode transfer, encoding conversion)
  4. Report a bug to the library only if cabextract succeeds but this reader fails
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-validate: an independent tool round-trip
const t = Bun.$`cabextract -t ${path}`.quiet().nothrow();
if ((await t).exitCode !== 0) throw new Error(`malformed cabinet: ${path}`);

Try / catch

try {
  await readCab(source, opts);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('folder data is shorter'))
    throw new Error('Cabinet file table is inconsistent with its data — file is malformed.');
  throw err;
}

Prevention

When it happens

Trigger: readCab() built entries where some CFFILE's folderOffset+usize exceeded the sum of all CFDATA cbUncomp values for the folder; a malformed or tampered cabinet where the file table and data blocks disagree.

Common situations: Corrupted or deliberately malformed cabinets; cabinets produced by buggy third-party writers that mis-declare uncompressed sizes; fuzz-tested inputs.

Related errors


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