can1357/oh-my-pi · error · ArchiveError
Unsupported multi-volume CAB archive: split CFDATA block
Error message
Unsupported multi-volume CAB archive: split CFDATA block
What it means
A CFDATA block with an uncompressed-size field of 0 signals a split block — the entry's data continues in the next volume of a multi-volume CAB set. This library supports only single-volume archives, so split blocks are rejected with a clear 'Unsupported multi-volume' error rather than silently returning partial file contents. Note the trigger is the uncompressed field being exactly 0, not the compressed field.
Source
Thrown at packages/utils/src/ar/cab.ts:150
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`);
}
}
outputSize += uncompressed;
assertInMemorySize(outputSize, this.#limits);
position = payloadEnd;
}View on GitHub (pinned to 9690622007)
Solutions
- Obtain and concatenate all volumes of the CAB set, then merge them into a single archive before reading (e.g. with `mscab` tooling or by copying files out with cabextract per volume and repacking).
- Extract the readable files from the available volume with `cabextract` and repack them into one single-volume CAB.
- If the data genuinely fits, recreate the archive with a size under the volume limit so no split blocks are emitted.
- Check your acquisition pipeline to ensure every span of the set is downloaded/kept.
- If only part of the set will ever be available, switch to a format (zip) that does not require whole-set availability.
Example fix
// before: reading only the first span
await readCabArchive(disk1); // Unsupported multi-volume CAB archive
// after: gather all spans and merge externally
cabextract disk1.cab disk2.cab -d out/
lcab out/ merged.cab
await readCabArchive(await Bun.file('merged.cab').bytes()); Defensive patterns
Strategy: try-catch
Validate before calling
// Detect a spanned set up front: volume headers/labels or a sibling-file check
const spans = await findSiblingSpans(cabPath); // e.g. base.1.cab, base.2.cab
if (spans.total > 1) throw new Error('Multi-volume CAB set detected — merge all spans before reading'); Try / catch
try {
return await readCabArchive(volume);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('multi-volume')) {
throw new Error('This CAB is one disk of a spanned set — supply all volumes and merge first');
}
throw err;
} Prevention
- Keep every span of a CAB set together when archiving/distributing.
- Prefer single-volume CABs (or zip) for programmatic pipelines.
- Detect span naming patterns (.1.cab/.2.cab, disk labels) at ingestion.
- Extract and merge with cabextract before in-process reading when spans are unavoidable.
When it happens
Trigger: readAll() on a CAB that is one disk/span of a multi-volume set, where the final CFDATA block of a folder is marked split (cbUncomp == 0).
Common situations: CABs split across floppy images or spanned installer media; only one part of a .cab/.1.cab/.2.cab set was copied; MakeCAB /SET output spanning multiple disks; download managers that fetched only the first span.
Related errors
- Unsupported multi-volume CAB archive (previous/next cabinet
- Multi-volume ARJ archives are unsupported
- Multi-volume ARJ members are unsupported
- Invalid CAB archive: metadata range is out of bounds
- Unable to read CAB archive: ${error instanceof Error ? error
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5eef232e878a39f5.
Report an issue: GitHub.