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

  1. Read the appended cause (text after 'Unable to read CAB archive: ') and fix the underlying I/O problem (permissions, disk space, missing file).
  2. Verify the source path exists and is readable before constructing the CAB reader: fs.access(path, fs.constants.R_OK).
  3. Keep the file handle/stream open for the lifetime of the reader — do not close the descriptor before calling header()/fileTable().
  4. If using a custom ArchiveSource, wrap known failures in ArchiveError yourself or ensure read() only throws for genuine read failures.
  5. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/fef991c59773d11b. Report an issue: GitHub.