can1357/oh-my-pi · error · ArchiveError
Invalid CAB archive: expected ${CAB_SIGNATURE} signature
Error message
Invalid CAB archive: expected ${CAB_SIGNATURE} signature What it means
readCabArchive reads the 36-byte fixed CFHEADER and checks its first four bytes for the 'MSCF' cabinet signature. If those bytes differ, the file is not a Microsoft CAB archive, so parsing is aborted with an ArchiveError before any further structure is trusted. This is the library's first-line sanity check against feeding it a completely wrong file format.
Source
Thrown at packages/utils/src/ar/cab.ts:246
}
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);
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)");View on GitHub (pinned to 9690622007)
Solutions
- Verify the file path points to an actual .cab file; check the first 4 bytes are 'MSCF' before calling readCab.
- If the cabinet is embedded inside an MSI/EXE, locate the inner cabinet offset and pass a source sliced at that offset rather than the wrapper itself.
- Re-download or re-export the file; a truncated-to-36+ bytes non-CAB blob usually means transfer corruption or the wrong artifact.
- Check whether the file is a related Microsoft format (e.g. MSI, MSP) and use the appropriate reader instead.
Example fix
// before
const entries = await readCab(Bun.file("setup.msi"));
// after
const buf = new Uint8Array(await Bun.file("data1.cab").arrayBuffer());
if (new TextDecoder().decode(buf.slice(0, 4)) !== "MSCF") throw new Error("not a CAB file");
const entries = await readCab(buf); Defensive patterns
Strategy: validation
Validate before calling
const head = new Uint8Array(buf.slice(0, 4));
const isCab = head[0] === 0x4d && head[1] === 0x53 && head[2] === 0x43 && head[3] === 0x46;
if (!isCab) throw new Error(`Not a CAB archive (got magic ${Buffer.from(head).toString("hex")})`); Type guard
function looksLikeCab(bytes: Uint8Array): boolean {
return bytes.length >= 4 && bytes[0] === 0x4d && bytes[1] === 0x53 && bytes[2] === 0x43 && bytes[3] === 0x46;
} Try / catch
try {
const entries = await readCab(source);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes("expected MSCF signature")) {
// wrong file type: surface a clear "not a CAB" message to the user
return null;
}
throw err;
} Prevention
- Check the 'MSCF' magic before handing any file to readCab.
- For embedded cabinets, slice the source at the verified inner-cabinet offset first.
- Verify download integrity (size/checksum) before parsing.
When it happens
Trigger: Calling readCab() (which delegates to readCabArchive) on a file whose first 4 bytes are not 4D 53 43 46 ('MSCF') — e.g. a ZIP, MSI container, or any non-CAB binary — while the file is at least FIXED_HEADER_SIZE (36) bytes long so the truncation check passes first.
Common situations: Pointing the reader at the wrong file in an installer directory, extracting CAB payloads embedded in MSI/EXE wrappers without locating the inner cabinet offset, downloading a corrupted or HTML-error-page file, or confusing MSZIP-style archives with actual CAB format.
Related errors
- Invalid CAB archive: reserved CFHEADER fields must be zero
- Unsupported CAB format version ${fixed[25]}.${fixed[24]} (ex
- Invalid CAB archive: files exist without a folder
- 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/5dbc3d60dc451728.
Report an issue: GitHub.