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
- Reject the archive as corrupt or malicious
- Rescan with a different extraction tool to confirm the chain is bogus
- 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
- Configure FormatReadOptions.limits for your environment
- Treat limit trips as security events and log them
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
- Invalid LZH extended header size
- Archive path cannot contain '..'
- Unsafe embedded addon archive entry: ${filename}
- Archive entry escapes extraction directory: ${archivePath}
- Corrupt compress (.Z) dictionary chain
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/0becfcc74f5bd531.
Report an issue: GitHub.