can1357/oh-my-pi · error · ArchiveError
Unable to read CAB archive: ${error instanceof Error ? error
Error message
Unable to read CAB archive: ${error instanceof Error ? error.message : String(error)} What it means
This ArchiveError wraps any unexpected failure from the underlying byte-source's read() while readExact fetches a region of a CAB archive. The library deliberately converts non-ArchiveError read failures (I/O errors, permission denials, closed streams) into a uniform 'Unable to read CAB archive: <cause>' message so callers only need to handle ArchiveError. The original error's message is appended for diagnosis.
Source
Thrown at packages/utils/src/ar/cab.ts:50
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;
let offset = 0;
while (offset + 4 <= bytes.byteLength) {
checksum ^= readUInt32LE(bytes, offset);
offset += 4;
}
const remaining = bytes.byteLength - offset;
let remainder = 0;View on GitHub (pinned to 9690622007)
Solutions
- Read the appended cause (text after 'Unable to read CAB archive: ') and fix the underlying I/O problem (permissions, disk space, missing file).
- Verify the source path exists and is readable before constructing the CAB reader: fs.access(path, fs.constants.R_OK).
- Keep the file handle/stream open for the lifetime of the reader — do not close the descriptor before calling header()/fileTable().
- If using a custom ArchiveSource, wrap known failures in ArchiveError yourself or ensure read() only throws for genuine read failures.
- Retry once if the source is transient (network mount flake) after confirming it is accessible.
Example fix
// before: reading from a stream closed earlier
const reader = await openCab(path);
stream.close();
await reader.fileTable(); // throws Unable to read CAB archive: EBADF
// after: read while handle is open
const reader = await openCab(path);
try {
await reader.fileTable();
} finally {
await stream.close();
} Defensive patterns
Strategy: try-catch
Validate before calling
import { accessSync, constants } from 'node:fs';
try { accessSync(path, constants.R_OK); } catch { throw new Error(`CAB source unreadable: ${path}`); } Type guard
function isArchiveError(e: unknown): e is ArchiveError {
return e instanceof ArchiveError;
} Try / catch
try {
const table = await reader.fileTable();
} catch (err) {
if (err instanceof ArchiveError && err.message.startsWith('Unable to read CAB archive:')) {
const cause = err.message.slice('Unable to read CAB archive: '.length);
console.error(`CAB source read failed: ${cause}`);
} else throw err;
} Prevention
- Keep the source handle open until all CAB reads complete.
- Check file readability (access) before opening the archive.
- Prefer plain file paths over volatile network mounts for archive sources.
- For custom ArchiveSource implementations, wrap expected failures in ArchiveError.
When it happens
Trigger: Calling header(), bytes(), fixed(), reserveHeader(), or fileTable() when source.read(start, end) rejects with something other than ArchiveError — e.g. the backing file descriptor was closed, the file was deleted mid-read, or a network-backed source dropped its connection.
Common situations: Reading a .cab from an NFS/cloud mount that goes away; passing a Blob/File handle whose underlying file was moved; a custom ArchiveSource whose read() throws a non-ArchiveError like ENOSPC or EACCES; reading a file being concurrently written/truncated by another process.
Related errors
- Unable to read ARJ archive: ${error instanceof Error ? error
- Invalid ARJ archive: truncated data
- Failed to read ASAR member '${formatArchivePathForError(memb
- 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/fef991c59773d11b.
Report an issue: GitHub.