can1357/oh-my-pi · error · ArchiveError
Unsupported multi-volume CAB archive (previous/next cabinet
Error message
Unsupported multi-volume CAB archive (previous/next cabinet link)
What it means
CAB's CFHEADER flags bit 0 (prev cabinet) and bit 1 (next cabinet) indicate a multi-volume cabinet set linked via szCabinetPrev/szCabinetNext. The library handles only single-volume cabinets and throws ArchiveError when either bit is set.
Source
Thrown at packages/utils/src/ar/cab.ts:264
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)
throw new ArchiveError("Invalid CAB archive: files exist without a folder");
let headerReserveSize = 0;
let folderReserveSize = 0;
let dataReserveSize = 0;
let folderTableOffset = FIXED_HEADER_SIZE;
if (flags & 0x0004) {
const reserveHeader = await readExact(source, FIXED_HEADER_SIZE, FIXED_HEADER_SIZE + 4, cabinetSize);
headerReserveSize = readUInt16LE(reserveHeader, 0);
folderReserveSize = reserveHeader[2]!;
dataReserveSize = reserveHeader[3]!;
if (headerReserveSize > 60_000)
throw new ArchiveError("Invalid CAB archive: CFHEADER reserve area exceeds 60000 bytes");
folderTableOffset += 4 + headerReserveSize;
}
const folderRecordSize = 8 + folderReserveSize;View on GitHub (pinned to 9690622007)
Solutions
- Locate the first volume of the set and merge/concatenate the volumes with a dedicated splitter tool before reading, if the producer supports it.
- Extract each volume's contents separately with cabextract and process them as independent archives.
- Repackage the data into a single self-contained cabinet (e.g. with makecab without spanning) if you control the source.
- If you only need entries from one volume, verify that volume's flags bit is clear before reading.
Example fix
// before
const entries = await readCab(await Bun.file("disk2.cab").arrayBuffer()); // flags set: part of spanned set
// after
import { $ } from "bun";
await $`cabextract -d out disk1.cab disk2.cab`.quiet(); // extract volumes with a spanning-aware tool Defensive patterns
Strategy: validation
Validate before calling
const buf = new Uint8Array(await Bun.file(path).arrayBuffer());
const flags = buf[30] | (buf[31]! << 8);
if (flags & 0x0003) throw new Error("Multi-volume CAB set detected; extract volumes with cabextract first"); Try / catch
try {
const entries = await readCab(source);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes("multi-volume CAB")) {
// delegate to cabextract or process each volume separately
return null;
}
throw err;
} Prevention
- Detect the multi-volume flags (0x0003) up front and route those files to a spanning-aware tool.
- When packaging, avoid cabinet spanning; keep data in a single self-contained volume.
- Document that this reader handles single-volume cabinets only.
When it happens
Trigger: Calling readCab() on a CAB where the flags word (offset 30) has 0x0001 or 0x0002 set — i.e. part of a split .cab set such as disk-spanned installers (data1.cab/data2.cab style sets produced by MakeCAB /i or DIAMOND).
Common situations: Multi-disk software distributions from the 90s/2000s, large InstallShield/MSI package sets split across several .cab files, and archives where a continuation cabinet references its neighbors.
Related errors
- Multi-volume ARJ archives are unsupported
- Multi-volume ARJ members are unsupported
- Unsupported multi-volume CAB archive: split CFDATA block
- Multi-volume LZH archives are unsupported
- Unsupported multi-volume RAR4 archive
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/ba93dfa1563a1d25.
Report an issue: GitHub.