can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: files exist without a folder

Error message

Invalid CAB archive: files exist without a folder

What it means

A CAB must contain at least one CFFOLDER for its CFFILE entries to belong to; every file entry references a folder index. The library rejects a cabinet that declares zero folders while still declaring one or more files, since such a file count is internally inconsistent.

Source

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

	}
	const cabinetSize = readUInt32LE(fixed, 8);
	if (cabinetSize < FIXED_HEADER_SIZE || cabinetSize > source.size) {
		throw new ArchiveError("Invalid CAB archive: declared cabinet size is out of bounds");
	}
	const fileTableOffset = readUInt32LE(fixed, 16);
	if (fileTableOffset < FIXED_HEADER_SIZE || fileTableOffset > cabinetSize) {
		throw new ArchiveError("Invalid CAB archive: CFFILE table offset is out of bounds");
	}
	if (fixed[24] !== 3 || fixed[25] !== 1) {
		throw new ArchiveError(`Unsupported CAB format version ${fixed[25]}.${fixed[24]} (expected 1.3)`);
	}
	const folderCount = readUInt16LE(fixed, 26);
	const fileCount = readUInt16LE(fixed, 28);
	const flags = readUInt16LE(fixed, 30);
	if (flags & 0x0003) throw new ArchiveError("Unsupported multi-volume CAB archive (previous/next cabinet link)");
	assertEntryCount(folderCount + fileCount, options.limits);
	if (folderCount === 0 && fileCount !== 0)
		throw new ArchiveError("Invalid CAB archive: files exist without a folder");

	let headerReserveSize = 0;
	let folderReserveSize = 0;
	let dataReserveSize = 0;
	let folderTableOffset = FIXED_HEADER_SIZE;
	if (flags & 0x0004) {
		const reserveHeader = await readExact(source, FIXED_HEADER_SIZE, FIXED_HEADER_SIZE + 4, cabinetSize);
		headerReserveSize = readUInt16LE(reserveHeader, 0);
		folderReserveSize = reserveHeader[2]!;
		dataReserveSize = reserveHeader[3]!;
		if (headerReserveSize > 60_000)
			throw new ArchiveError("Invalid CAB archive: CFHEADER reserve area exceeds 60000 bytes");
		folderTableOffset += 4 + headerReserveSize;
	}
	const folderRecordSize = 8 + folderReserveSize;
	const folderTableEnd = folderTableOffset + folderCount * folderRecordSize;
	if (!Number.isSafeInteger(folderTableEnd) || folderTableEnd > cabinetSize || folderTableEnd > fileTableOffset) {
		throw new ArchiveError("Invalid CAB archive: CFFOLDER table is out of bounds");

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the archive with cabextract; an inconsistent header means the file should be re-obtained.
  2. If you write CAB files, always emit at least one CFFOLDER record and set cFolders accordingly.
  3. Check whether the file was damaged in transit by comparing checksums with the original source.
  4. An empty-but-valid cabinet should have cFolders >= 1 and cFiles = 0; if you intend an empty archive, fix the writer.
Defensive patterns

Strategy: validation

Validate before calling

const buf = new Uint8Array(await Bun.file(path).arrayBuffer());
const folderCount = buf[26] | (buf[27]! << 8);
const fileCount = buf[28] | (buf[29]! << 8);
if (folderCount === 0 && fileCount !== 0) throw new Error("Inconsistent CAB: files declared without any folder");

Try / catch

try {
	const entries = await readCab(source);
} catch (err) {
	if (err instanceof ArchiveError && err.message.includes("files exist without a folder")) {
		throw new Error("CAB header is internally inconsistent; archive is corrupt", { cause: err });
	}
	throw err;
}

Prevention

When it happens

Trigger: Calling readCab() on a signed CAB whose cFolders (offset 26) is 0 while cFiles (offset 28) is greater than 0 — a header inconsistency from corruption or a malformed writer.

Common situations: Hand-crafted or fuzzed archives, corrupted files where the folder count byte was zeroed, and buggy custom CAB writers that forgot to emit the (at least one) folder record.

Related errors


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