{"record":{"id":"c902508211220c1f","repo":"can1357/oh-my-pi","slug":"invalid-lzh-level-2-extended-header-chain","errorCode":null,"errorMessage":"Invalid LZH level-2 extended header chain","messagePattern":"Invalid LZH level-2 extended header chain","errorType":"validation","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":556,"sourceCode":"\t\tlet extensionCount = 0;\n\t\twhile (cursor + 2 <= headerEnd) {\n\t\t\tconst extensionSize = u16(bytes, cursor);\n\t\t\tif (extensionSize === 0) {\n\t\t\t\tcursor += 2;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (extensionSize < 3 || cursor + extensionSize > headerEnd)\n\t\t\t\tthrow new ArchiveError(\"Invalid LZH extended header size\");\n\t\t\tif (++extensionCount > 65_535) throw new ArchiveError(\"Invalid LZH archive: too many extended headers\");\n\t\t\tconst type = bytes[cursor + 2]!;\n\t\t\tconst data = bytes.subarray(cursor + 3, cursor + extensionSize);\n\t\t\tif (type === 0x01 || type === 0x02 || type === 0x44 || type === 0x45) {\n\t\t\t\tassertArchivePathBytes(data.byteLength, \"member path\", options.limits.maxPathBytes);\n\t\t\t}\n\t\t\tprocessExtendedHeader(type, data, cursor + 3, fields);\n\t\t\tcursor += extensionSize;\n\t\t}\n\t\tif (cursor !== headerEnd) throw new ArchiveError(\"Invalid LZH level-2 extended header chain\");\n\t\tdataStart = headerEnd;\n\t\tpackedSize = fields.packedSize ?? packedSize;\n\t}\n\tif (level === 1 && osId === 0x20 && method === \"-lh7-\") {\n\t\tthrow new ArchiveError(\"LZH uses the incompatible LHARK -lh7- variant\");\n\t}\n\n\tsize = fields.size ?? size;\n\tmtimeMs = fields.mtimeMs ?? mtimeMs;\n\tconst mode = fields.mode;\n\tif (fields.commonCrc !== undefined) {\n\t\tconst relative = fields.commonCrcOffset! - offset;\n\t\tif (crc16WithZeroRange(bytes.subarray(offset, dataStart), relative, relative + 2) !== fields.commonCrc) {\n\t\t\tthrow new ArchiveError(\"Invalid LZH common header CRC-16\");\n\t\t}\n\t}\n\tconst filename = fields.unicodeFilename ?? fields.filename ?? legacyFilename ?? \"\";\n\tconst directory = fields.unicodeDirectory ?? fields.directory ?? \"\";","sourceCodeStart":538,"sourceCodeEnd":574,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L538-L574","documentation":"After walking the level-2 extended-header chain, the cursor must land exactly on the header end: extensions tile the space contiguously, terminated by a zero size word. A cursor left short of headerEnd means the chain is malformed (overlapping/incorrect sizes that left unexplained trailing bytes).","triggerScenarios":"readLzh() on a level-2 archive where extended headers do not exactly fill the header region — e.g. extension sizes that skip bytes or a missing zero terminator within headerEnd.","commonSituations":"Archives written by buggy or non-conformant LZH implementations; partially corrupted files; hand-edited binaries.","solutions":["Re-extract or re-download the archive and validate against a known checksum.","Repack the archive with a conformant tool (lha/lhasa) so the extended-header chain is regenerated correctly.","If the file came from a legacy source, try opening it with a reference tool first to confirm whether it is valid LZH at all.","If processing untrusted input, catch ArchiveError and reject the file."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const bytes = await readAllBytes(source);\nif (!sniffLzh(bytes)) throw new Error(\"not LZH\");\nif ((await sha256File(path)) !== expectedSha256) throw new Error(\"archive integrity check failed\");","typeGuard":null,"tryCatchPattern":"try {\n  const entries = await readLzh(source, options);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message === \"Invalid LZH level-2 extended header chain\") {\n    throw new Error(\"LZH header chain malformed — repack with a standard LHA tool\");\n  }\n  throw err;\n}","preventionTips":["Create .lzh archives only with conformant tools (lha/lhasa)","Round-trip legacy archives through a modern tool once, then keep the repacked copy","Verify checksums before parsing"],"tags":["archive","lzh","corrupt-file","parsing"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}