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

  1. Verify the file against its original checksum; re-download or re-extract if it differs.
  2. Repack the archive with a standard LHA tool so the common CRC is regenerated over the current header bytes.
  3. If a third-party tool modified the archive (rename/recompress), redo the operation with a tool that maintains LZH common CRCs.
  4. 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

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


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