can1357/oh-my-pi · error · ArchiveError

Invalid LZH extended header size

Error message

Invalid LZH extended header size

What it means

Thrown while walking a level-1 LZH header's chain of extended headers when an extension records a size smaller than the 3-byte minimum (2-byte size field + 1 type byte). Such a record cannot exist in the format, so the header chain is corrupt.

Source

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

		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);
			let cursor = baseEnd;
			let totalExtensionSize = 0;
			let extensionCount = 0;
			while (extensionSize !== 0) {
				if (extensionSize < 3) throw new ArchiveError("Invalid LZH extended header size");
				assertRange(bytes, cursor, cursor + extensionSize, "extended header");
				totalExtensionSize += extensionSize;
				assertIndexSize(headerLength + 2 + totalExtensionSize, options.limits, "header metadata");
				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");

View on GitHub (pinned to 9690622007)

Solutions

  1. Obtain an intact copy of the archive
  2. Repack with a conforming LHA tool
  3. Reject the file if input is untrusted — the chain is unrecoverable without heuristics

Example fix

null
Defensive patterns

Strategy: try-catch

Try / catch

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

Prevention

When it happens

Trigger: Parsing an archive whose extended-header chain contains a zero-corrupted or miscomputed size field, or where a prior extension's size was wrong so the cursor lands mid-field.

Common situations: Damaged archives, non-conforming writers, or fuzzed inputs exercising the extension-chain loop.

Related errors


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