can1357/oh-my-pi · error · ArchiveError

Invalid LZH filename length

Error message

Invalid LZH filename length

What it means

Thrown when a level-0/1 LZH header's filename-length byte claims a name that would extend past the declared header length (22 + nameLength + 2 > headerLength + 2). The header is internally inconsistent — it does not have room for the advertised filename plus CRC.

Source

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

	let size = u32(bytes, offset + 11);
	let crc = 0;
	let legacyFilename: string | undefined;
	let mtimeMs: number | undefined;
	let osId = 0;
	let dataStart: number;
	const fields: LzhExtendedFields = {};

	if (level < 2) {
		const headerLength = bytes[offset]!;
		const minimum = level === 0 ? 22 : 25;
		if (headerLength < minimum) throw new ArchiveError(`Invalid LZH level-${level} header size`);
		const baseEnd = offset + headerLength + 2;
		assertRange(bytes, offset, baseEnd, "header");
		let sum = 0;
		for (let index = offset + 2; index < baseEnd; index++) sum = (sum + bytes[index]!) & 0xff;
		if (sum !== bytes[offset + 1]) throw new ArchiveError("Invalid LZH header checksum");
		const nameLength = bytes[offset + 21]!;
		if (22 + nameLength + 2 > headerLength + 2) throw new ArchiveError("Invalid LZH filename length");
		assertArchivePathBytes(nameLength, "member path", options.limits.maxPathBytes);
		legacyFilename = decodeLegacy(bytes.subarray(offset + 22, offset + 22 + nameLength));
		crc = u16(bytes, offset + 22 + nameLength);
		mtimeMs = dosTimeToMs(u32(bytes, offset + 15));
		if (level === 0) {
			const extendedStart = offset + 24 + nameLength;
			if (baseEnd - extendedStart >= 12) {
				const extended = bytes.subarray(extendedStart, baseEnd);
				if ((extended[0] === 0x55 || extended[0] === 0x4b) && extended[1] === 0) {
					osId = extended[0]!;
					fields.mtimeMs = u32(extended, 2) * 1000;
					fields.mode = u16(extended, extended.byteLength - 6);
				}
			}
			dataStart = baseEnd;
		} else {
			osId = bytes[offset + 24 + nameLength]!;
			let extensionSize = u16(bytes, baseEnd - 2);

View on GitHub (pinned to 9690622007)

Solutions

  1. Replace the archive with an intact copy
  2. Repack with a standard tool (lha, 7-zip)
  3. If the input is untrusted, treat this as a rejection signal — do not attempt recovery

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

const nameLength = bytes[21]; if (nameLength > 255 || 24 + nameLength > bytes[0] + 2) throw new Error('bad name length');

Try / catch

try { parse(); } catch (err) { if (err instanceof ArchiveError) reject(err.message); else throw err; }

Prevention

When it happens

Trigger: Reading an archive with a corrupt or hostile filename-length byte, or a header written by a tool that miscounts its own header size.

Common situations: Malformed archives from unreliable packers, fuzzed inputs, or truncated files where downstream bytes are zeros/garbage.

Related errors


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