can1357/oh-my-pi · error · ArchiveError
Invalid CAB archive: truncated CFDATA header
Error message
Invalid CAB archive: truncated CFDATA header
What it means
While walking description.blockCount CFDATA blocks inside a folder's data region, the reader checks that each block's fixed header (DATA_BLOCK_SIZE plus the folder's reserve area) fits in the bytes read from the source. When it does not, the folder claims more blocks than its data can hold, so the archive is declared truncated. This catches size-field lies in CFHEADER/CFFOLDER versus actual file length.
Source
Thrown at packages/utils/src/ar/cab.ts:146
const description = this.#description;
if (description.method === 2) {
throw new ArchiveError(`Unsupported CAB compression method: Quantum (level ${description.parameter})`);
}
if (description.method > 3) {
throw new ArchiveError(`Unsupported CAB compression method: ${description.method}`);
}
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`);
}
}View on GitHub (pinned to 9690622007)
Solutions
- Re-download/re-copy the .cab and verify its size or SHA against the publisher's checksum.
- Run `cabextract -t file.cab` to confirm truncation independently before debugging your code.
- Compare the folder's cbData and blockCount against the file's actual remaining length to identify which field is wrong.
- If the file is one volume of a set, ensure all volumes are present and you are reading the correct one (multi-volume CABs are rejected elsewhere).
- If you generate these CABs, fix the writer so blockCount/cbData match the emitted data.
Example fix
// before: trusting a partial download
await readCabArchive(await Bun.file('part.cab').bytes()); // truncated CFDATA header
// after: verify completeness first
const expected = 48317234;
const buf = await Bun.file('part.cab').bytes();
if (buf.byteLength < expected) throw new Error(`CAB incomplete: ${buf.byteLength}/${expected} bytes`);
await readCabArchive(buf); Defensive patterns
Strategy: validation
Validate before calling
const size = (await Bun.stat(cabPath)).size;
if (size !== expectedSize)
throw new Error(`CAB size mismatch: got ${size}, expected ${expectedSize} — download is truncated`); Try / catch
try {
return await readCabArchive(bytes);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('truncated CFDATA')) {
throw new Error(`CAB folder sizes exceed file length (${bytes.byteLength} bytes) — re-acquire the archive`);
}
throw err;
} Prevention
- Compare file size/checksum after every download before parsing.
- Complete multi-part downloads before opening any part.
- Verify shipped CAB fixtures once with cabextract -t.
- Watch for antivirus/proxy tooling that truncates or quarantines archive tails.
When it happens
Trigger: readAll() where a CFFOLDER's cbData/blockCount fields imply more or larger CFDATA headers than the bytes between dataStart and dataEnd — typically a CAB cut short or with corrupted folder sizes.
Common situations: Interrupted downloads of multi-hundred-MB .cab distribution files; antivirus quarantining the tail of a file; archives assembled by concatenating partial volumes; offsets corrupted by a bad disk sector.
Related errors
- Invalid CAB archive: truncated data
- Invalid CAB archive: truncated CFDATA payload
- Invalid CAB archive: CFDATA block ${block} checksum mismatch
- Invalid CAB archive: folder data is shorter than its file ta
- Invalid CAB archive: MSZIP block is missing its CK signature
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/492e94ff1155e3e6.
Report an issue: GitHub.