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.

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.

Appendix: 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)