can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: member '${memberPath}' is outside its f

Error message

Invalid CAB archive: member '${memberPath}' is outside its folder data

What it means

Thrown by CabMemberSource.read when the member's [offset, offset+size) range falls outside the folder's fully decoded byte buffer — negative offset, unsafe integer overflow, or an end past the folder length. This catches cabinets whose CFFILE folderOffset/size point outside the actual folder data, preventing out-of-range slices.

Source

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

		}
		return output;
	}
}

class CabMemberSource implements MemberSource {
	readonly #folder: CabFolder;
	readonly #offset: number;
	readonly #declaredSize: number;

	constructor(folder: CabFolder, offset: number, size: number) {
		this.#folder = folder;
		this.#offset = offset;
		this.#declaredSize = size;
	}

	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);

View on GitHub (pinned to 9690622007)

Solutions

  1. Validate the archive with `cabextract -t` or 7-Zip; if they reject it, the file is malformed — obtain a good copy
  2. If you generate cabinets, ensure each CFFILE's folderOffset+size stays within the folder's total decompressed bytes
  3. Treat this as a signal when processing untrusted archives: catch ArchiveError and reject the file rather than attempting repair
  4. If a standard tool accepts the file but this reader throws, file a bug with the repro archive
Defensive patterns

Strategy: validation

Validate before calling

// reject malformed cabinets up front with an independent parser
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('outside its folder data'))
    throw new Error('Cabinet member offsets point outside folder data — file is malformed.');
  throw err;
}

Prevention

When it happens

Trigger: readCab() indexed a malformed cabinet where a CFFILE entry's folderOffset+usize exceeds the folder's decoded output length (or offset is negative/end overflows); also reachable if folder data decoded shorter than the file table implied without tripping the requiredSize check.

Common situations: Malformed or corrupted cabinets; crafted archives from untrusted sources; archives produced by buggy writers with wrong CFFILE offsets.

Related errors


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