can1357/oh-my-pi · error · ArchiveError

Invalid LZH header checksum

Error message

Invalid LZH header checksum

What it means

Thrown when the single-byte additive checksum stored at offset+1 of a level-0/1 LZH header does not equal the sum (mod 256) of the header bytes from offset+2 through the end of the base header. This detects corruption in the header itself before any data is touched.

Source

Thrown at packages/utils/src/ar/lzh.ts:484

	if (!LHA_METHOD_PATTERN.test(method)) throw new ArchiveError(`Invalid LZH compression method '${method}'`);
	let packedSize = u32(bytes, offset + 7);
	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 {

View on GitHub (pinned to 9690622007)

Solutions

  1. Obtain an uncorrupted copy of the archive and compare checksums
  2. Recreate the archive with a standard LHA tool that computes the header sum
  3. If you generate LZH files yourself, fix the writer to recompute the checksum byte after any header edit

Example fix

// before: editing the filename bytes in the header directly
headerBytes[30] = 0x2d; // corrupts sum
// after: recompute the checksum after edits
for (let i = 2; i < headerEnd; i++) sum = (sum + headerBytes[i]) & 0xff;
headerBytes[1] = sum;
Defensive patterns

Strategy: validation

Validate before calling

let sum = 0; for (let i = 2; i < bytes[0] + 2; i++) sum = (sum + bytes[i]) & 0xff;
if (sum !== bytes[1]) throw new Error('header checksum mismatch pre-check');

Try / catch

try { parse(); } catch (err) { if (err instanceof ArchiveError && err.message.includes('checksum')) reject(err); else throw err; }

Prevention

When it happens

Trigger: Reading an archive whose header bytes were modified or corrupted after creation (the checksum covers method, sizes, dates, filename).

Common situations: Files damaged by download/storage, archives edited by hand or by buggy tools that forgot to recompute the header checksum byte.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/2df6f5aa22f50261. Report an issue: GitHub.