can1357/oh-my-pi · error · ArchiveError
Invalid CAB archive: CFDATA expands to ${uncompressed} bytes
Error message
Invalid CAB archive: CFDATA expands to ${uncompressed} bytes (maximum 32768) What it means
Each CFDATA block must expand to at most 32768 bytes (the CAB spec's cbUncomp maximum). If the block's stored uncompressed size exceeds MAX_DATA_OUTPUT, the header is lying or corrupt, so the reader aborts with this error before allocating or decoding — this also bounds memory use against malicious files.
Source
Thrown at packages/utils/src/ar/cab.ts:152
}
if (description.method === 3 && (description.parameter < 15 || description.parameter > 21)) {
throw new ArchiveError(`Unsupported CAB LZX window size: ${description.parameter} bits (expected 15-21)`);
}
const compressedSize = description.dataEnd - description.dataStart;
assertInMemorySize(compressedSize, this.#limits);
const bytes = await readExact(this.#source, description.dataStart, description.dataEnd);
let position = 0;
let outputSize = 0;
for (let block = 0; block < description.blockCount; block++) {
if (position + DATA_BLOCK_SIZE + this.#dataReserveSize > bytes.byteLength) {
throw new ArchiveError("Invalid CAB archive: truncated CFDATA header");
}
const compressed = readUInt16LE(bytes, position + 4);
const uncompressed = readUInt16LE(bytes, position + 6);
if (uncompressed === 0) throw new ArchiveError("Unsupported multi-volume CAB archive: split CFDATA block");
if (uncompressed > MAX_DATA_OUTPUT) {
throw new ArchiveError(`Invalid CAB archive: CFDATA expands to ${uncompressed} bytes (maximum 32768)`);
}
const payloadStart = position + DATA_BLOCK_SIZE + this.#dataReserveSize;
const payloadEnd = payloadStart + compressed;
if (payloadEnd > bytes.byteLength) throw new ArchiveError("Invalid CAB archive: truncated CFDATA payload");
const expectedChecksum = readUInt32LE(bytes, position);
if (expectedChecksum !== 0) {
const payloadChecksum = cabChecksum(bytes.subarray(payloadStart, payloadEnd));
const actualChecksum = cabChecksum(bytes.subarray(position + 4, payloadStart), payloadChecksum);
if (actualChecksum !== expectedChecksum) {
throw new ArchiveError(`Invalid CAB archive: CFDATA block ${block} checksum mismatch`);
}
}
outputSize += uncompressed;
assertInMemorySize(outputSize, this.#limits);
position = payloadEnd;
}
if (outputSize < description.requiredSize) {
throw new ArchiveError("Invalid CAB archive: folder data is shorter than its file table declares");View on GitHub (pinned to 9690622007)
Solutions
- Verify the archive independently (`cabextract -t`); if it extracts fine externally, the block is out-of-spec — re-create the CAB with a standard tool.
- Re-download the file and check its checksum; a single-region corruption can zero/FF out cbUncomp.
- Hex-dump the failing CFDATA header (cbChecksum, cbCompressed at +4, cbUncomp at +6) to see whether 0xFFFF is isolated corruption.
- If you produce these CABs, fix the packer so no CFDATA block's uncompressed size exceeds 32768 bytes.
- Keep this check as-is in your deployment: it is a deliberate zip-bomb-style guard and should not be bypassed.
Example fix
// before: reading a corrupt CAB
await readCabArchive(buf); // CFDATA expands to 65535 bytes
// after: verify integrity and re-obtain
if ((await Bun.hash(await Bun.file('fresh.cab').arrayBuffer())) !== expectedHash)
throw new Error('CAB failed checksum verification');
await readCabArchive(await Bun.file('fresh.cab').bytes()); Defensive patterns
Strategy: try-catch
Validate before calling
// cbUncomp is a 16-bit LE field; anything above 32768 means corruption (only 0xFFFF can exceed)
const cbUncomp = view.getUint16(cfdataOffset + 6, true);
if (cbUncomp > 32768)
throw new Error(`CFDATA cbUncomp=${cbUncomp} exceeds the 32768 spec maximum — archive is corrupt`); Type guard
function isLegalCfdataSize(uncomp: number): boolean {
return uncomp >= 0 && uncomp <= 32768;
} Try / catch
try {
return await readCabArchive(bytes);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('CFDATA expands to')) {
throw new Error('Corrupt CFDATA block size (likely 0xFFFF) — re-download or re-pack the CAB');
}
throw err;
} Prevention
- Verify checksums after transfer; single-region corruption triggers this error.
- Never bypass the 32768 block cap — it is a memory/zip-bomb guard.
- Use spec-compliant CAB writers so no block exceeds 32 KB uncompressed.
- Treat this error as corruption first, hostile input second — both warrant rejection.
When it happens
Trigger: readAll() encountering a CFDATA block whose cbUncomp field (read at position+6, little-endian uint16) is > 32768. Since the field is 16-bit, the only possible offending value is 0xFFFF (65535).
Common situations: Corruption where 0xFFFF sentinel/garbage replaced cbUncomp; crafted archives attempting to blow up allocation; buggy writers that stored sizes > 32 KB per block by violating the spec.
Related errors
- Invalid CAB archive: truncated data
- Invalid CAB archive: file has an invalid DOS timestamp
- Unsupported CAB LZX window size: ${windowBits} bits (expecte
- Invalid CAB archive: LZX frame size ${outputSize} exceeds 32
- Invalid ARJ ${field}: missing terminator
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/94633c27d57fed40.
Report an issue: GitHub.