can1357/oh-my-pi · error · ArchiveError
Invalid CAB archive: metadata range is out of bounds
Error message
Invalid CAB archive: metadata range is out of bounds
What it means
readExact is CAB's guard for every metadata read: it validates that [start, end) is a sane range — safe integers, non-negative, ordered, and within the cabinet's size — before issuing the read. This rejects CAB files whose header/folder/file-table fields point outside the file (common with corruption or crafted archives), converting them into a uniform ArchiveError instead of an I/O error downstream.
Source
Thrown at packages/utils/src/ar/cab.ts:43
const LEGACY_NAME_DECODER = new TextDecoder("windows-1252");
interface CabFolderDescription {
dataStart: number;
dataEnd: number;
blockCount: number;
method: number;
parameter: number;
requiredSize: number;
}
async function readExact(
source: ByteSource,
start: number,
end: number,
cabinetSize = source.size,
): Promise<Uint8Array> {
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > cabinetSize) {
throw new ArchiveError("Invalid CAB archive: metadata range is out of bounds");
}
let bytes: Uint8Array;
try {
bytes = await source.read(start, end);
} catch (error) {
if (error instanceof ArchiveError) throw error;
throw new ArchiveError(`Unable to read CAB archive: ${error instanceof Error ? error.message : String(error)}`);
}
if (bytes.byteLength !== end - start) throw new ArchiveError("Invalid CAB archive: truncated data");
return bytes;
}
function hasSignature(bytes: Uint8Array): boolean {
return bytes.byteLength >= 4 && bytes[0] === 0x4d && bytes[1] === 0x53 && bytes[2] === 0x43 && bytes[3] === 0x46;
}
function cabChecksum(bytes: Uint8Array, initial = 0): number {
let checksum = initial >>> 0;View on GitHub (pinned to 9690622007)
Solutions
- Verify the file is complete — compare actual byte length against the declared cbCabinet in the CFHEADER and re-download if truncated.
- Check the ByteSource's `size` reflects the real file size; a wrong size makes valid ranges look out of bounds.
- Inspect the offsets being requested; if they grow implausibly during parsing, earlier structure parsing is likely misaligned.
- Treat the cabinet as invalid/corrupt and refuse to process it — the library intentionally refuses partial metadata reads.
Example fix
// before
const table = await readExact(source, fileTableOffset, fileTableOffset + tableSize); // throws if offset+size > source.size
// after
if (fileTableOffset + tableSize > source.size) {
throw new Error(`truncated cabinet: file table ends at ${fileTableOffset + tableSize} but file is ${source.size} bytes`);
}
const table = await readExact(source, fileTableOffset, fileTableOffset + tableSize); Defensive patterns
Strategy: try-catch
Validate before calling
function isReadableRange(start, end, cabinetSize) {
return Number.isSafeInteger(start) && Number.isSafeInteger(end) &&
start >= 0 && end >= start && end <= cabinetSize;
}
// before reading metadata:
if (!isReadableRange(fileTableOffset, fileTableOffset + tableSize, source.size)) {
throw new Error("cabinet metadata out of bounds — file likely truncated or corrupt");
} Type guard
function isSafeRange(start, end, limit) {
return Number.isSafeInteger(start) && Number.isSafeInteger(end) &&
start >= 0 && end >= start && end <= limit;
} Try / catch
try {
const header = await readExact(source, 0, 42);
} catch (err) {
if (err instanceof ArchiveError) {
if (err.message.includes("out of bounds")) throw new Error("invalid or truncated CAB archive");
if (err.message.includes("truncated data")) throw new Error("CAB file is incomplete");
}
throw err;
} Prevention
- Check file size against the CFHEADER cbCabinet field before parsing folders/entries.
- Validate every offset read from the header lies within the file before dereferencing it.
- Handle partial downloads: verify checksums or total length before extraction.
- Never trust offsets from untrusted archives without bounding them to source.size.
When it happens
Trigger: Parsing a CAB whose CFFOLDER/CFFILE offsets or cbCabinet sizes exceed the actual file size; a truncated cabinet where declared metadata runs past EOF; an offset read at a misaligned position after earlier parse drift; passing an explicit cabinetSize argument smaller than the ranges being requested.
Common situations: Partially downloaded or truncated .cab files; corrupted Windows update payloads; malicious archives with inflated offset fields; mounting archives through a ByteSource whose `size` disagrees with the real content length.
Related errors
- Invalid RPM package: tag ${tag} points outside header data
- Invalid RPM package: string tag ${tag} exceeds header data
- Invalid RPM package: tag ${tag} exceeds header data
- Invalid ARJ archive: truncated ${what}
- Invalid ARJ ${field}: missing terminator
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/01389e02b3369701.
Report an issue: GitHub.