can1357/oh-my-pi · error · ArchiveError

Multi-volume LZH archives are unsupported

Error message

Multi-volume LZH archives are unsupported

What it means

Extended header type 0x39 marks a multi-volume (split/disk-spanning) LZH archive. This library reads single in-memory archives only and cannot resolve members continued across volumes, so it refuses the archive up front.

Source

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

	type: number,
	data: Uint8Array,
	absoluteDataOffset: number,
	fields: LzhExtendedFields,
): void {
	switch (type) {
		case 0x00:
			if (data.byteLength < 2) throw new ArchiveError("Invalid LZH common extended header");
			fields.commonCrc = u16(data, 0);
			fields.commonCrcOffset = absoluteDataOffset;
			break;
		case 0x01:
			fields.filename = decodeLegacy(data);
			break;
		case 0x02:
			fields.directory = decodeLegacy(data).replaceAll("ÿ", "/");
			break;
		case 0x39:
			throw new ArchiveError("Multi-volume LZH archives are unsupported");
		case 0x41:
			if (data.byteLength >= 16) {
				const low = u32(data, 8);
				const high = u32(data, 12);
				const filetime = low + high * 0x100000000;
				if (Number.isSafeInteger(filetime)) fields.mtimeMs = filetime / 10_000 - 11_644_473_600_000;
			}
			break;
		case 0x42:
			if (data.byteLength < 16) throw new ArchiveError("Invalid LZH 64-bit size extended header");
			fields.packedSize = u64(data, 0);
			fields.size = u64(data, 8);
			break;
		case 0x44:
			fields.unicodeFilename = decodeUtf16(data);
			break;
		case 0x45:
			fields.unicodeDirectory = decodeUtf16(data).replaceAll("ÿ", "/");

View on GitHub (pinned to 9690622007)

Solutions

  1. Supply all volumes of the set and rejoin/split-merge them into a single archive using a tool that supports multi-volume LHA (e.g. LHA/UNLHA32 on the original media) before loading
  2. Re-create the archive as a single-volume .lzh (or a different format like zip) with all members included
  3. If you only need specific members, extract them with an external multi-volume-aware tool first, then repack individually
  4. Detect volume numbering in your app (e.g. .lzh/.l00/.l01 suffixes) and prompt the user to merge before calling the reader

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

// Detect likely multi-volume LHA sets before calling the reader
import * as fs from "node:fs";
function looksLikeMultiVolumeSet(basePath: string): boolean {
	return /\.l(?:zh|ha|[0-9]{2})$/i.test(basePath) &&
		fs.existsSync(basePath.replace(/\.l(?:zh|ha)$/i, ".l00"));
}

Type guard

null

Try / catch

try {
	const entries = await readArchive(lzhBytes);
} catch (err) {
	if (err instanceof ArchiveError && err.message.includes("Multi-volume")) {
		// prompt user to merge volumes with a multi-volume-aware tool first
	}
	throw err;
}

Prevention

When it happens

Trigger: Reading a .lzh file that is one part of a multi-disk/multi-volume set created with disk spanning (e.g. old backup sets spanning floppies or numbered .lzh/.l00/.l01 files).

Common situations: Recovering legacy backup archives split across disks; users supplying only the first volume of a set; archival migration of multi-part LHA files.

Related errors


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