can1357/oh-my-pi · error · ArchiveError
Invalid LZH common header CRC-16
Error message
Invalid LZH common header CRC-16
What it means
Level-1 headers can carry a common header CRC-16 (an extended-header field with its offset into the header). The library recomputes the CRC-16 over the header with the CRC field zeroed and compares it. A mismatch means the header bytes were altered or the field/offset is inconsistent with the actual header layout.
Source
Thrown at packages/utils/src/ar/lzh.ts:570
}
processExtendedHeader(type, data, cursor + 3, fields);
cursor += extensionSize;
}
if (cursor !== headerEnd) throw new ArchiveError("Invalid LZH level-2 extended header chain");
dataStart = headerEnd;
packedSize = fields.packedSize ?? packedSize;
}
if (level === 1 && osId === 0x20 && method === "-lh7-") {
throw new ArchiveError("LZH uses the incompatible LHARK -lh7- variant");
}
size = fields.size ?? size;
mtimeMs = fields.mtimeMs ?? mtimeMs;
const mode = fields.mode;
if (fields.commonCrc !== undefined) {
const relative = fields.commonCrcOffset! - offset;
if (crc16WithZeroRange(bytes.subarray(offset, dataStart), relative, relative + 2) !== fields.commonCrc) {
throw new ArchiveError("Invalid LZH common header CRC-16");
}
}
const filename = fields.unicodeFilename ?? fields.filename ?? legacyFilename ?? "";
const directory = fields.unicodeDirectory ?? fields.directory ?? "";
const rawPath = `${directory}${directory && !/[\\/]$/.test(directory) ? "/" : ""}${filename}`;
assertArchivePathString(rawPath, "member path", options.limits.maxPathBytes);
assertArchiveMemberSize(size, rawPath || "<unnamed>", options.limits);
assertRange(bytes, dataStart, dataStart + packedSize, "member data");
return {
method,
packedSize,
size,
dataStart,
nextOffset: dataStart + packedSize,
crc,
path: normalizeArchiveEntryPath(rawPath),
mtimeMs,
mode,View on GitHub (pinned to 9690622007)
Solutions
- Verify the file against its original checksum; re-download or re-extract if it differs.
- Repack the archive with a standard LHA tool so the common CRC is regenerated over the current header bytes.
- If a third-party tool modified the archive (rename/recompress), redo the operation with a tool that maintains LZH common CRCs.
- Treat the archive as untrusted/corrupt and reject it rather than attempting extraction.
Defensive patterns
Strategy: validation
Validate before calling
if ((await sha256File(path)) !== expectedSha256) throw new Error("archive modified or corrupt — refusing to parse"); Try / catch
try {
const entries = await readLzh(source, options);
} catch (err) {
if (err instanceof ArchiveError && err.message === "Invalid LZH common header CRC-16") {
throw new Error("archive header was modified or corrupt — restore from a verified copy");
}
throw err;
} Prevention
- Keep archives immutable after creation; never patch header bytes in place
- Verify checksums before parsing
- If renaming members, repack with a tool that updates LZH CRCs
When it happens
Trigger: readLzh() on a level-1 archive whose extended headers advertise a common CRC (fields.commonCrc) that does not match the recomputed CRC-16 over the header region.
Common situations: Bit-rot or corruption of header bytes; tools that rewrite headers (e.g. renaming members) without updating the common CRC; hand-patched archives.
Related errors
- ARJ member '${memberPath}' failed CRC32 verification
- Invalid XZ stream: block CRC32 mismatch
- LZH member '${memberPath}' failed CRC-16 verification
- Invalid LZH level-2 extended header chain
- Invalid LZH archive: header did not advance
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/908843ef6ac9592a.
Report an issue: GitHub.