can1357/oh-my-pi · error · ArchiveError
Invalid LZH filename length
Error message
Invalid LZH filename length
What it means
Thrown when a level-0/1 LZH header's filename-length byte claims a name that would extend past the declared header length (22 + nameLength + 2 > headerLength + 2). The header is internally inconsistent — it does not have room for the advertised filename plus CRC.
Source
Thrown at packages/utils/src/ar/lzh.ts:486
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;
fields.mode = u16(extended, extended.byteLength - 6);
}
}
dataStart = baseEnd;
} else {
osId = bytes[offset + 24 + nameLength]!;
let extensionSize = u16(bytes, baseEnd - 2);View on GitHub (pinned to 9690622007)
Solutions
- Replace the archive with an intact copy
- Repack with a standard tool (lha, 7-zip)
- If the input is untrusted, treat this as a rejection signal — do not attempt recovery
Example fix
null
Defensive patterns
Strategy: validation
Validate before calling
const nameLength = bytes[21]; if (nameLength > 255 || 24 + nameLength > bytes[0] + 2) throw new Error('bad name length'); Try / catch
try { parse(); } catch (err) { if (err instanceof ArchiveError) reject(err.message); else throw err; } Prevention
- Bound-check header fields yourself for untrusted input
- Reject on first inconsistency
When it happens
Trigger: Reading an archive with a corrupt or hostile filename-length byte, or a header written by a tool that miscounts its own header size.
Common situations: Malformed archives from unreliable packers, fuzzed inputs, or truncated files where downstream bytes are zeros/garbage.
Related errors
- Invalid ${label} Huffman table: oversubscribed codes
- Invalid ${label} Huffman table: incomplete codes
- Invalid ${label} temporary Huffman table
- Invalid LZH archive: truncated ${what}
- Invalid LZH compression method '${method}'
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4104f8662fec2299.
Report an issue: GitHub.