{"record":{"id":"f23bead15f9f58e0","repo":"can1357/oh-my-pi","slug":"invalid-lzh-unicode-path-header-odd-utf-16-length","errorCode":null,"errorMessage":"Invalid LZH Unicode path header: odd UTF-16 length","messagePattern":"Invalid LZH Unicode path header: odd UTF-16 length","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":315,"sourceCode":"\tif (\n\t\t!Number.isSafeInteger(start) ||\n\t\t!Number.isSafeInteger(end) ||\n\t\tstart < 0 ||\n\t\tend < start ||\n\t\tend > bytes.byteLength\n\t) {\n\t\tthrow new ArchiveError(`Invalid LZH archive: truncated ${what}`);\n\t}\n}\n\nfunction decodeLegacy(bytes: Uint8Array): string {\n\tlet end = bytes.indexOf(0);\n\tif (end < 0) end = bytes.byteLength;\n\treturn LEGACY_DECODER.decode(bytes.subarray(0, end));\n}\n\nfunction decodeUtf16(bytes: Uint8Array): string {\n\tif ((bytes.byteLength & 1) !== 0) throw new ArchiveError(\"Invalid LZH Unicode path header: odd UTF-16 length\");\n\tlet end = bytes.byteLength;\n\twhile (end >= 2 && bytes[end - 1] === 0 && bytes[end - 2] === 0) end -= 2;\n\treturn UTF16LE_DECODER.decode(bytes.subarray(0, end));\n}\n\nfunction dosTimeToMs(value: number): number | undefined {\n\tif (value === 0) return undefined;\n\tconst year = 1980 + ((value >>> 25) & 0x7f);\n\tconst month = (value >>> 21) & 0x0f;\n\tconst day = (value >>> 16) & 0x1f;\n\tconst hour = (value >>> 11) & 0x1f;\n\tconst minute = (value >>> 5) & 0x3f;\n\tconst second = (value & 0x1f) * 2;\n\tif (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59) return undefined;\n\treturn Date.UTC(year, month - 1, day, hour, minute, second);\n}\n\ninterface LzhExtendedFields {","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L297-L333","documentation":"LZH level-1/2 headers can carry Unicode (UTF-16LE) filename/directory extended headers (types 0x44/0x45). This library requires those payloads to contain a whole number of UTF-16 code units; an odd byte count means the archive is malformed, so decodeUtf16 throws before decoding.","triggerScenarios":"Reading an LZH archive whose member header contains a 0x44 (Unicode filename) or 0x45 (Unicode directory) extended header whose data section has an odd number of bytes — e.g. the header size field was corrupted, truncated, or written by a tool that computed the extension length incorrectly.","commonSituations":"Corrupted or partially downloaded .lzh/.lha files; archives produced by non-conformant or buggy Japanese archivers (e.g. old UNLHA32-compatible tools) that miscount extended-header sizes; hand-patched headers.","solutions":["Verify the archive with its original source (re-download or re-extract) — odd-length UTF-16 payloads indicate corruption, not a recoverable path","Try extracting the same archive with a tolerant tool (e.g. 7-Zip or LHA) to confirm which member is damaged; if only one member is bad, skip/repair it upstream","If you generate archives, fix the writer so the 0x44/0x45 extended-header size includes the 2-byte size trailer and yields an even data length","As a workaround, strip the Unicode extended headers from the archive (convert paths to the legacy 0x01/0x02 headers)"],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// UTF-16 extended-header data must have even byte length\nfunction isValidUtf16HeaderData(data: Uint8Array): boolean {\n\treturn (data.byteLength & 1) === 0;\n}","typeGuard":"null","tryCatchPattern":"import { ArchiveError } from \"@oh-my-pi/pi-utils/ar/error\";\ntry {\n\tconst entries = await readArchive(lzhBytes);\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message.includes(\"odd UTF-16 length\")) {\n\t\t// archive has corrupt Unicode path headers — treat as damaged, request a fresh copy\n\t}\n\tthrow err;\n}","preventionTips":["Verify archive checksums at ingest time to catch corruption before parsing","Reject or quarantine archives that fail an external-tool validation pass (e.g. `lha l`) first","Avoid hand-editing LZH header bytes; always repack via a standard tool"],"tags":["archive","lzh","corrupt-header","encoding"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}