can1357/oh-my-pi · error · ArchiveError
Invalid LZH level-${level} header size
Error message
Invalid LZH level-${level} header size What it means
Thrown when a level-0 or level-1 LZH header's declared length byte is smaller than the minimum structurally required size (22 bytes for level 0, 25 for level 1). The header cannot possibly contain the mandatory fields, so parsing aborts.
Source
Thrown at packages/utils/src/ar/lzh.ts:479
function parseLzhHeader(bytes: Uint8Array, offset: number, options: FormatReadOptions): ParsedLzhHeader {
assertRange(bytes, offset, offset + 22, "header");
const level = bytes[offset + 20]!;
if (level > 2) throw new ArchiveError(`Unsupported LZH header level ${level}`);
const method = String.fromCharCode(...bytes.subarray(offset + 2, offset + 7));
if (!LHA_METHOD_PATTERN.test(method)) throw new ArchiveError(`Invalid LZH compression method '${method}'`);
let packedSize = u32(bytes, offset + 7);
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;View on GitHub (pinned to 9690622007)
Solutions
- Replace the archive with an intact copy and verify its size/checksum
- Repack the source files into a fresh LHA archive
- If processing untrusted input, catch ArchiveError and reject the file
Example fix
// before: trusting any byte blob as a member
for (const member of lzhMembers(bytes)) { ... }
// after: guard the read
try {
for (const member of lzhMembers(bytes)) { ... }
} catch (err) {
if (err instanceof ArchiveError) throw new Error(`corrupt LZH: ${err.message}`);
throw err;
} Defensive patterns
Strategy: validation
Validate before calling
if (bytes.byteLength < 24) throw new Error('too small to be an LZH member'); Try / catch
try { parse(); } catch (err) { if (err instanceof ArchiveError && /header size/.test(err.message)) skip(err); else throw err; } Prevention
- Validate minimum sizes before parse
- Track archive provenance
When it happens
Trigger: Reading an archive with a truncated or corrupt header-length byte, or a forged/minimized header written by a broken tool.
Common situations: Partially downloaded .lzh files, archives damaged in storage, or fuzzed/malicious inputs.
Related errors
- Invalid LZH header checksum
- Invalid LZH level-2 header size
- Invalid ARJ archive: missing main header
- Invalid ARJ main header
- Invalid ${label} Huffman table: oversubscribed codes
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2d8a2bd0265c3b79.
Report an issue: GitHub.