{"record":{"id":"c51ee05e1bf9ee50","repo":"can1357/oh-my-pi","slug":"invalid-lzh-archive-header-did-not-advance","errorCode":null,"errorMessage":"Invalid LZH archive: header did not advance","messagePattern":"Invalid LZH archive: header did not advance","errorType":"validation","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/lzh.ts","lineNumber":623,"sourceCode":"\tlet bytes: Uint8Array;\n\ttry {\n\t\tbytes = await readAllBytes(source);\n\t} catch (error) {\n\t\tif (error instanceof ArchiveError) throw error;\n\t\tthrow new ArchiveError(`Unable to read LZH archive: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tif (bytes.byteLength !== source.size) throw new ArchiveError(\"Invalid LZH archive: truncated data\");\n\tif (!sniffLzh(bytes)) throw new ArchiveError(\"Invalid LZH archive header\");\n\tconst entries: ArchiveIndexEntry[] = [];\n\tlet offset = 0;\n\tlet parsedCount = 0;\n\tlet metadataSize = 0;\n\twhile (offset < bytes.byteLength && bytes[offset] !== 0) {\n\t\tconst header = parseLzhHeader(bytes, offset, options);\n\t\tmetadataSize += header.dataStart - offset;\n\t\tassertIndexSize(metadataSize, options.limits, \"index\");\n\t\tassertEntryCount(++parsedCount, options.limits);\n\t\tif (header.nextOffset <= offset) throw new ArchiveError(\"Invalid LZH archive: header did not advance\");\n\t\toffset = header.nextOffset;\n\t\tif (!header.path) continue;\n\t\tconst isDirectory = header.method === \"-lhd-\";\n\t\tif (isDirectory && header.mode !== undefined && (header.mode & 0xf000) === 0xa000) {\n\t\t\tconst separator = header.path.indexOf(\"|\");\n\t\t\tif (separator < 1) throw new ArchiveError(`Invalid LZH symbolic link '${header.path}'`);\n\t\t\tconst path = normalizeArchiveEntryPath(header.path.slice(0, separator));\n\t\t\tconst targetPath = normalizeArchiveEntryPath(header.path.slice(separator + 1));\n\t\t\tif (!path || !targetPath) continue;\n\t\t\tentries.push({\n\t\t\t\tpath,\n\t\t\t\tisDirectory: false,\n\t\t\t\tsize: 0,\n\t\t\t\tmtimeMs: header.mtimeMs,\n\t\t\t\tmode: header.mode,\n\t\t\t\tstorage: { type: \"link\", targetPath, resolveTarget: false },\n\t\t\t});\n\t\t\tcontinue;","sourceCodeStart":605,"sourceCodeEnd":641,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/lzh.ts#L605-L641","documentation":"LZH members are chained: each header's nextOffset (dataStart + packedSize) must strictly advance past the current offset, otherwise the parser would loop forever. When a header's computed packed size is zero or wraps backwards, the chain cannot progress and the archive is declared corrupt.","triggerScenarios":"readLzh() on an archive where a member's nextOffset <= current offset: packedSize computed as 0 or negative-to-wrap, corrupt size fields, or an extended-header packedSize override of 0.","commonSituations":"Corrupted or truncated archives; crafted inputs targeting parser infinite loops; archives damaged by bit-flips on storage.","solutions":["Validate the archive with an external tool (lha l) to see which member is corrupt.","Re-download or restore the archive from backup and re-verify its checksum.","Extract recoverable members up to the corruption point if partial content is acceptable (parse manually member by member).","Reject untrusted input that triggers this error — it usually indicates malicious or badly damaged data."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const entries = await readLzh(source, options);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message === \"Invalid LZH archive: header did not advance\") {\n    throw new Error(\"archive chain corrupt at a member boundary — restore from backup\");\n  }\n  throw err;\n}","preventionTips":["Validate archives with an external tool (lha l) before batch processing","Keep checksums for stored archives and verify before parsing","Isolate parsing of untrusted archives so a hang/corruption cannot affect the host"],"tags":["archive","lzh","corrupt-file","infinite-loop-protection"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}