can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: metadata range is out of bounds

Error message

Invalid CAB archive: metadata range is out of bounds

What it means

readExact is CAB's guard for every metadata read: it validates that [start, end) is a sane range — safe integers, non-negative, ordered, and within the cabinet's size — before issuing the read. This rejects CAB files whose header/folder/file-table fields point outside the file (common with corruption or crafted archives), converting them into a uniform ArchiveError instead of an I/O error downstream.

Source

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

const LEGACY_NAME_DECODER = new TextDecoder("windows-1252");

interface CabFolderDescription {
	dataStart: number;
	dataEnd: number;
	blockCount: number;
	method: number;
	parameter: number;
	requiredSize: number;
}

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;

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the file is complete — compare actual byte length against the declared cbCabinet in the CFHEADER and re-download if truncated.
  2. Check the ByteSource's `size` reflects the real file size; a wrong size makes valid ranges look out of bounds.
  3. Inspect the offsets being requested; if they grow implausibly during parsing, earlier structure parsing is likely misaligned.
  4. Treat the cabinet as invalid/corrupt and refuse to process it — the library intentionally refuses partial metadata reads.

Example fix

// before
const table = await readExact(source, fileTableOffset, fileTableOffset + tableSize); // throws if offset+size > source.size
// after
if (fileTableOffset + tableSize > source.size) {
  throw new Error(`truncated cabinet: file table ends at ${fileTableOffset + tableSize} but file is ${source.size} bytes`);
}
const table = await readExact(source, fileTableOffset, fileTableOffset + tableSize);
Defensive patterns

Strategy: try-catch

Validate before calling

function isReadableRange(start, end, cabinetSize) {
  return Number.isSafeInteger(start) && Number.isSafeInteger(end) &&
    start >= 0 && end >= start && end <= cabinetSize;
}
// before reading metadata:
if (!isReadableRange(fileTableOffset, fileTableOffset + tableSize, source.size)) {
  throw new Error("cabinet metadata out of bounds — file likely truncated or corrupt");
}

Type guard

function isSafeRange(start, end, limit) {
  return Number.isSafeInteger(start) && Number.isSafeInteger(end) &&
    start >= 0 && end >= start && end <= limit;
}

Try / catch

try {
  const header = await readExact(source, 0, 42);
} catch (err) {
  if (err instanceof ArchiveError) {
    if (err.message.includes("out of bounds")) throw new Error("invalid or truncated CAB archive");
    if (err.message.includes("truncated data")) throw new Error("CAB file is incomplete");
  }
  throw err;
}

Prevention

When it happens

Trigger: Parsing a CAB whose CFFOLDER/CFFILE offsets or cbCabinet sizes exceed the actual file size; a truncated cabinet where declared metadata runs past EOF; an offset read at a misaligned position after earlier parse drift; passing an explicit cabinetSize argument smaller than the ranges being requested.

Common situations: Partially downloaded or truncated .cab files; corrupted Windows update payloads; malicious archives with inflated offset fields; mounting archives through a ByteSource whose `size` disagrees with the real content length.

Related errors


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