can1357/oh-my-pi · error · ArchiveError

LZH uses sizes too large to read safely

Error message

LZH uses sizes too large to read safely

What it means

u64 combines two 32-bit little-endian words into a JS number; if the result exceeds Number.MAX_SAFE_INTEGER, sizes can no longer be compared or used for allocation safely. The library throws rather than proceeding with an imprecise size.

Source

Thrown at packages/utils/src/ar/lzh.ts:292

function crc16WithZeroRange(bytes: Uint8Array, zeroStart: number, zeroEnd: number): number {
	if (zeroStart < 0) return crc16Arc(bytes);
	let value = crc16Arc(bytes.subarray(0, zeroStart));
	value = crc16Arc(ZERO_CRC16_BYTES.subarray(0, zeroEnd - zeroStart), value);
	return crc16Arc(bytes.subarray(zeroEnd), value);
}

function u16(bytes: Uint8Array, offset: number): number {
	return bytes[offset]! | (bytes[offset + 1]! << 8);
}

function u32(bytes: Uint8Array, offset: number): number {
	return (bytes[offset]! | (bytes[offset + 1]! << 8) | (bytes[offset + 2]! << 16) | (bytes[offset + 3]! << 24)) >>> 0;
}

function u64(bytes: Uint8Array, offset: number): number {
	const value = u32(bytes, offset) + u32(bytes, offset + 4) * 0x100000000;
	if (!Number.isSafeInteger(value)) throw new ArchiveError("LZH uses sizes too large to read safely");
	return value;
}

function assertRange(bytes: Uint8Array, start: number, end: number, what: string): void {
	if (
		!Number.isSafeInteger(start) ||
		!Number.isSafeInteger(end) ||
		start < 0 ||
		end < start ||
		end > bytes.byteLength
	) {
		throw new ArchiveError(`Invalid LZH archive: truncated ${what}`);
	}
}

function decodeLegacy(bytes: Uint8Array): string {
	let end = bytes.indexOf(0);
	if (end < 0) end = bytes.byteLength;

View on GitHub (pinned to 9690622007)

Solutions

  1. Check the extended-header offset/length parsing before the u64 read
  2. Re-validate the archive (CRC) and re-download
  3. Treat as corrupt: catch ArchiveError and skip the entry
Defensive patterns

Strategy: try-catch

Validate before calling

// Reject implausible sizes before header processing
if (packed.length < 12) throw new Error("archive too small for extended header");

Try / catch

try {
  const entries = readArchive(packed);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("too large")) {
    // header offsets are likely wrong or the file is corrupt
    return null;
  }
  throw err;
}

Prevention

When it happens

Trigger: processExtendedHeader reading a 64-bit size field (common in modern LZH extended headers) whose value exceeds ~9 PB — practically always corrupt/misaligned data rather than a genuine size.

Common situations: Extended-header parsing at a wrong offset so garbage is read as the high 32 bits; fuzzed archives; very old archives misinterpreted as 64-bit sizes.

Related errors


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