can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: reserved CFHEADER fields must be zero

Error message

Invalid CAB archive: reserved CFHEADER fields must be zero

What it means

The CFHEADER contains three reserved 32-bit fields (offsets 4, 12, and 20) that the CAB specification requires to be zero. The library rejects any cabinet where any of these is non-zero, since a valid writer never sets them and non-zero values indicate a corrupt or hand-crafted file.

Source

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

	async read(size: number, memberPath: string): Promise<Uint8Array> {
		if (size !== this.#declaredSize) {
			throw new ArchiveError(`Invalid CAB archive: size changed while extracting '${memberPath}'`);
		}
		const folder = await this.#folder.readAll();
		const end = this.#offset + size;
		if (!Number.isSafeInteger(end) || this.#offset < 0 || end > folder.byteLength) {
			throw new ArchiveError(`Invalid CAB archive: member '${memberPath}' is outside its folder data`);
		}
		return folder.slice(this.#offset, end);
	}
}

async function readCabArchive(source: ByteSource, options: Parameters<FormatReader>[1]): Promise<ArchiveIndexEntry[]> {
	if (source.size < FIXED_HEADER_SIZE) throw new ArchiveError("Invalid CAB archive: truncated CFHEADER");
	const fixed = await readExact(source, 0, FIXED_HEADER_SIZE);
	if (!hasSignature(fixed)) throw new ArchiveError(`Invalid CAB archive: expected ${CAB_SIGNATURE} signature`);
	if (readUInt32LE(fixed, 4) !== 0 || readUInt32LE(fixed, 12) !== 0 || readUInt32LE(fixed, 20) !== 0) {
		throw new ArchiveError("Invalid CAB archive: reserved CFHEADER fields must be zero");
	}
	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)

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-obtain the archive from a trusted source; the header is corrupt and cannot be safely repaired by the caller.
  2. Inspect header bytes 4-7, 12-15, and 20-23 with a hex dump to confirm which reserved field is non-zero.
  3. If the file comes from an in-house generator, fix the writer to zero the reserved fields per the MS-CAB specification.
  4. Test the cabinet with a reference tool (e.g. cabextract) to see whether it is generally considered corrupt.
Defensive patterns

Strategy: validation

Validate before calling

import { readUInt32LE } from "@oh-my-pi/pi-utils/ar/bytes";
const fixed = new Uint8Array(buf.slice(0, 36));
if (fixed[4] !== 0 || fixed[5] !== 0 || fixed[6] !== 0 || fixed[7] !== 0 ||
	fixed[12] !== 0 || fixed[13] !== 0 || fixed[14] !== 0 || fixed[15] !== 0 ||
	fixed[20] !== 0 || fixed[21] !== 0 || fixed[22] !== 0 || fixed[23] !== 0) {
	throw new Error("CAB reserved header fields are non-zero; file is corrupt");
}

Try / catch

try {
	const entries = await readCab(source);
} catch (err) {
	if (err instanceof ArchiveError && err.message.includes("reserved CFHEADER fields")) {
		logger.warn("CAB header corrupt; skipping archive", { path });
		return null;
	}
	throw err;
}

Prevention

When it happens

Trigger: Calling readCab() on a file with a valid 'MSCF' signature but non-zero data at header offsets 4, 12, or 20 — typically a bit-flipped, partially overwritten, or maliciously malformed cabinet.

Common situations: Corrupted downloads or disk-level damage that only flipped a few header bytes, fuzzed or hand-edited test files, and cabinets assembled by broken third-party tooling that writes garbage into reserved fields.

Related errors


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