can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: truncated data

Error message

Invalid CAB archive: truncated data

What it means

readExact verifies that source.read(start, end) returned exactly end-start bytes; if fewer bytes came back the CAB file is shorter than its structure demands and the archive is declared truncated. CAB files carry offset/size fields, so a short file fails this exact-length check. This is a strict validation: partial reads are never accepted.

Source

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

}

async function readExact(
	source: ByteSource,
	start: number,
	end: number,
	cabinetSize = source.size,
): Promise<Uint8Array> {
	if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > cabinetSize) {
		throw new ArchiveError("Invalid CAB archive: metadata range is out of bounds");
	}
	let bytes: Uint8Array;
	try {
		bytes = await source.read(start, end);
	} catch (error) {
		if (error instanceof ArchiveError) throw error;
		throw new ArchiveError(`Unable to read CAB archive: ${error instanceof Error ? error.message : String(error)}`);
	}
	if (bytes.byteLength !== end - start) throw new ArchiveError("Invalid CAB archive: truncated data");
	return bytes;
}

function hasSignature(bytes: Uint8Array): boolean {
	return bytes.byteLength >= 4 && bytes[0] === 0x4d && bytes[1] === 0x53 && bytes[2] === 0x43 && bytes[3] === 0x46;
}

function cabChecksum(bytes: Uint8Array, initial = 0): number {
	let checksum = initial >>> 0;
	let offset = 0;
	while (offset + 4 <= bytes.byteLength) {
		checksum ^= readUInt32LE(bytes, offset);
		offset += 4;
	}
	const remaining = bytes.byteLength - offset;
	let remainder = 0;
	if (remaining === 3) {
		remainder = (bytes[offset]! << 16) | (bytes[offset + 1]! << 8) | bytes[offset + 2]!;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download or re-copy the .cab file and compare its byte size (or checksum) against the original source.
  2. Verify the file starts with the MSCF signature (0x4D 0x53 0x43 0x46) — hasSignature() — to confirm it is actually a CAB.
  3. Check the file size on disk; if it is smaller than expected for this archive, the transfer was cut short.
  4. If using a custom ArchiveSource, ensure read() throws instead of returning short buffers when EOF is hit mid-range.
  5. Try extracting with an external tool (cabextract -t) to confirm the archive itself is intact before blaming your code.

Example fix

// before: trusting a possibly-incomplete download
const reader = await openCab(downloadedPath);
await reader.fileTable(); // Invalid CAB archive: truncated data
// after: verify size/signature first
const stat = await fs.stat(downloadedPath);
if (stat.size < expectedMinSize) throw new Error('CAB download incomplete, re-fetching');
const reader = await openCab(downloadedPath);
await reader.fileTable();
Defensive patterns

Strategy: validation

Validate before calling

const stat = await fs.stat(cabPath);
if (stat.size < 8) throw new Error(`File too small to be a CAB: ${stat.size} bytes`);
const head = new Uint8Array(await Bun.file(cabPath).slice(0, 4).arrayBuffer());
if (!(head[0] === 0x4d && head[1] === 0x53 && head[2] === 0x43 && head[3] === 0x46))
  throw new Error('Missing MSCF signature — not a CAB file');

Type guard

function hasCabSignature(b: Uint8Array): boolean {
  return b.byteLength >= 4 && b[0] === 0x4d && b[1] === 0x53 && b[2] === 0x43 && b[3] === 0x46;
}

Try / catch

try {
  return await readCabArchive(bytes);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('truncated data')) {
    throw new Error(`CAB file is incomplete (${bytes.byteLength} bytes) — re-download it`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Any header(), bytes(), fixed(), reserveHeader(), or fileTable() call where the underlying source returns a Uint8Array with byteLength < end - start — most often a file smaller than the header sizes/offsets declared inside it, or an offset pointing past EOF.

Common situations: Incomplete download of a .cab file; a truncated transfer (HTTP 200 body cut short); a corrupted/corrupted-by-antivirus archive; a file renamed to .cab that is not actually a CAB; passing the wrong start/end offsets to a custom source that silently clamps reads.

Related errors


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