can1357/oh-my-pi · error · ArchiveError
Invalid CAB archive: declared cabinet size is out of bounds
Error message
Invalid CAB archive: declared cabinet size is out of bounds
What it means
The CFHEADER declares the total cabinet size at offset 8. The library requires this value to be at least the fixed header size (36) and no larger than the actual source size; otherwise the declared layout cannot be trusted and further reads could go out of bounds.
Source
Thrown at packages/utils/src/ar/cab.ts:252
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)");
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;View on GitHub (pinned to 9690622007)
Solutions
- Check the file is fully downloaded/transferred: compare its byte length against the size recorded in the producing tool's manifest.
- Hex-dump bytes 8-11 and compare the little-endian value to the actual file size; if it exceeds it, the file is truncated.
- Re-export or re-download the archive; the library will not guess at a corrected size.
- If the cabinet is genuinely multi-part, ensure you have the complete single volume rather than a partial member.
Example fix
// before
const entries = await readCab(partialBuffer); // truncated download
// after
const stat = await fs.stat("data1.cab");
if (stat.size !== expectedSize) throw new Error("incomplete download");
const entries = await readCab(await Bun.file("data1.cab").arrayBuffer()); Defensive patterns
Strategy: validation
Validate before calling
const stat = await Bun.file(path).stat?.() ?? (await import("node:fs/promises")).stat(path);
const buf = new Uint8Array(await Bun.file(path).arrayBuffer());
const declared = buf[8] | (buf[9]! << 8) | (buf[10]! << 16) | (buf[11]! << 24) >>> 0;
if (declared > buf.byteLength) throw new Error(`Truncated CAB: header declares ${declared} bytes, file has ${buf.byteLength}`); Try / catch
try {
const entries = await readCab(source);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes("declared cabinet size is out of bounds")) {
throw new Error("CAB file is truncated or incomplete; re-download it", { cause: err });
}
throw err;
} Prevention
- Compare file size against the expected size before parsing.
- Never parse archives from partially streamed/prefixed buffers; pass the complete bytes.
- Verify downloads with checksums when available.
When it happens
Trigger: Calling readCab() on a signed CAB whose cbCabinet field (bytes 8-11) is zero, negative-wrapped, or larger than the file — e.g. the file was truncated after the header was written, or the size field was zeroed by corruption.
Common situations: Partially downloaded or truncated .cab files, streaming sources where only a prefix was buffered, archives copied with an incorrect byte count, or fuzzed inputs with an inflated size field.
Related errors
- Invalid CAB archive: CFFILE table offset is out of bounds
- Invalid CAB archive: CFFOLDER table is out of bounds
- Invalid CAB archive: CFFOLDER data offset is out of bounds
- ASAR member '${formatArchivePathForError(memberPath)}' is tr
- Invalid CAB archive: metadata range is out of bounds
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4fdce225405301e4.
Report an issue: GitHub.