can1357/oh-my-pi · error · ArchiveError

Invalid LZH level-2 header size

Error message

Invalid LZH level-2 header size

What it means

Thrown when a level-2 LZH header's 16-bit declared length is smaller than the 26-byte minimum required for its fixed fields. The header cannot contain the mandatory level-2 layout (CRC, OS id, timestamps), so parsing aborts.

Source

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

				if (++extensionCount > 65_535) throw new ArchiveError("Invalid LZH archive: too many extended headers");
				const type = bytes[cursor]!;
				const dataEnd = cursor + extensionSize - 2;
				const data = bytes.subarray(cursor + 1, dataEnd);
				if (type === 0x01 || type === 0x02 || type === 0x44 || type === 0x45) {
					assertArchivePathBytes(data.byteLength, "member path", options.limits.maxPathBytes);
				}
				processExtendedHeader(type, data, cursor + 1, fields);
				const currentSize = extensionSize;
				extensionSize = u16(bytes, dataEnd);
				cursor += currentSize;
			}
			dataStart = baseEnd + totalExtensionSize;
			packedSize = fields.packedSize ?? packedSize - totalExtensionSize;
			if (packedSize < 0) throw new ArchiveError("Invalid LZH level-1 packed size");
		}
	} else {
		const headerLength = u16(bytes, offset);
		if (headerLength < 26) throw new ArchiveError("Invalid LZH level-2 header size");
		const headerEnd = offset + headerLength;
		assertRange(bytes, offset, headerEnd, "header");
		crc = u16(bytes, offset + 21);
		osId = bytes[offset + 23]!;
		mtimeMs = u32(bytes, offset + 15) * 1000;
		let cursor = offset + 24;
		let extensionCount = 0;
		while (cursor + 2 <= headerEnd) {
			const extensionSize = u16(bytes, cursor);
			if (extensionSize === 0) {
				cursor += 2;
				break;
			}
			if (extensionSize < 3 || cursor + extensionSize > headerEnd)
				throw new ArchiveError("Invalid LZH extended header size");
			if (++extensionCount > 65_535) throw new ArchiveError("Invalid LZH archive: too many extended headers");
			const type = bytes[cursor + 2]!;
			const data = bytes.subarray(cursor + 3, cursor + extensionSize);

View on GitHub (pinned to 9690622007)

Solutions

  1. Obtain an uncorrupted copy of the archive
  2. Repack with a conforming tool (e.g. lha -w2 or a level-2 capable packer)
  3. Catch ArchiveError and reject the input if processing untrusted files

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

const headerLength = bytes[0] | (bytes[1] << 8); if (headerLength < 26) throw new Error('level-2 header too small');

Try / catch

try { parse(); } catch (err) { if (err instanceof ArchiveError && err.message.includes('level-2')) reject(err); else throw err; }

Prevention

When it happens

Trigger: Reading a level-2 LHA archive with a corrupt or truncated header-length field, or a file misidentified as level-2 LZH.

Common situations: Damaged downloads, archives from buggy level-2 writers, fuzzed or hostile inputs.

Related errors


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