can1357/oh-my-pi · error · ArchiveError

LZH member '${memberPath}' uses unsupported dynamic-Huffman

Error message

LZH member '${memberPath}' uses unsupported dynamic-Huffman method -lh1-

What it means

-lh1- uses dynamic Huffman tables (the older LZHUF scheme), which this decoder does not implement; it only supports the static-Huffman -lh4-..-lh7- methods plus stored/-lzs-. Members compressed with -lh1- are rejected explicitly.

Source

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

				output = packed.slice();
				break;
			case "-lh4-":
				output = decompressLhStatic(packed, size, 1 << 12, 4, 13, "LZH -lh4-");
				break;
			case "-lh5-":
				output = decompressLhStatic(packed, size, 1 << 13, 4, 14, "LZH -lh5-");
				break;
			case "-lh6-":
				output = decompressLhStatic(packed, size, 1 << 15, 5, 16, "LZH -lh6-");
				break;
			case "-lh7-":
				output = decompressLhStatic(packed, size, 1 << 16, 5, 17, "LZH -lh7-");
				break;
			case "-lzs-":
				output = decompressLzs(packed, size);
				break;
			case "-lh1-":
				throw new ArchiveError(`LZH member '${memberPath}' uses unsupported dynamic-Huffman method -lh1-`);
			default:
				throw new ArchiveError(`LZH member '${memberPath}' uses unsupported compression method ${this.#method}`);
		}
		if (output.byteLength !== size)
			throw new ArchiveError(`LZH member '${memberPath}' extracted to an unexpected size`);
		if (crc16Arc(output) !== this.#crc)
			throw new ArchiveError(`LZH member '${memberPath}' failed CRC-16 verification`);
		return output;
	}
}

interface ParsedLzhHeader {
	method: string;
	packedSize: number;
	size: number;
	dataStart: number;
	nextOffset: number;
	crc: number;

View on GitHub (pinned to 9690622007)

Solutions

  1. Recompress the archive with a modern tool (e.g. `lha a -lh5` or 7-Zip) so members use -lh5-/-lh6-/-lh7-, then read the repacked archive
  2. Extract -lh1- members with an external tool that supports them (lhasa, UNLHA32, The Unarchiver) and feed the extracted files to your app instead
  3. Detect the method before extraction (method bytes at header offset+2) and warn users about legacy -lh1- content
  4. If -lh1- support is essential for your use case, file an issue/request on the library

Example fix

// before
const entries = await readArchive(lzhBytes); // throws on -lh1- members
// after
import { sniffLzh } from "@oh-my-pi/pi-utils/ar/lzh";
const method = String.fromCharCode(...lzhBytes.subarray(2, 7));
if (method === "-lh1-") {
	const entries = await repackWithModernTool(lzhBytes); // e.g. lha -> -lh5-
	return readArchive(entries);
}
return readArchive(lzhBytes);
Defensive patterns

Strategy: fallback

Validate before calling

// Inspect the method bytes of the first member before reading
function lzhMethod(bytes: Uint8Array): string {
	return String.fromCharCode(...bytes.subarray(2, 7));
}

Type guard

function isUnsupportedDynamicMethod(method: string): boolean {
	return method === "-lh1-" || method === "-lh2-" || method === "-lh3-";
}

Try / catch

try {
	return await readArchive(lzhBytes);
} catch (err) {
	if (err instanceof ArchiveError && err.message.includes("-lh1-")) {
		// fallback: recompress/extract externally, then retry
		const repacked = await repackWithExternalTool(lzhBytes);
		return readArchive(repacked);
	}
	throw err;
}

Prevention

When it happens

Trigger: Reading an LZH archive whose member was compressed with method -lh1- (common in very old LHA files and the original LHarc output) and then extracting/reading that member.

Common situations: Opening 1990s-era LHA archives; files created by legacy LHarc 1.x; migrating old Japanese software distributions that used -lh1-.

Related errors


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