can1357/oh-my-pi · error · ArchiveError

Invalid LZH archive: too many extended headers

Error message

Invalid LZH archive: too many extended headers

What it means

A safety-limit error thrown when a level-1 LZH header's extended-header chain exceeds 65,535 records. Legitimate archives never need this many extensions; an unbounded or cyclic chain indicates corruption or a hostile input designed to exhaust resources.

Source

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

				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");
		}
	} else {
		const headerLength = u16(bytes, offset);
		if (headerLength < 26) throw new ArchiveError("Invalid LZH level-2 header size");

View on GitHub (pinned to 9690622007)

Solutions

  1. Reject the archive as corrupt or malicious
  2. Rescan with a different extraction tool to confirm the chain is bogus
  3. If it is your own writer's output, fix its extension-size bookkeeping so the chain terminates with a 0x0000 terminator

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

enforceSizeLimit(bytes, 64 * 1024 * 1024);

Try / catch

try { parse(); } catch (err) { if (err instanceof ArchiveError && err.message.includes('too many')) flagMalicious(err); else throw err; }

Prevention

When it happens

Trigger: Parsing an archive whose extension chain never terminates properly (a size loop pointing back on itself) or an intentionally crafted zip-bomb-style header.

Common situations: Processing untrusted uploads, scanning archives from unknown origins, or files corrupted so the chain cursor cycles.

Related errors


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