can1357/oh-my-pi · error · ArchiveError

Invalid ARJ extended header CRC32

Error message

Invalid ARJ extended header CRC32

What it means

Like basic headers, each ARJ extended header carries a CRC-32 over its payload, stored after the payload. parseArjBlock computes crc32 over the extension bytes and compares against the trailing u32; a mismatch means that extension's bytes are corrupted, so parsing stops rather than consuming untrusted extended metadata.

Source

Thrown at packages/utils/src/ar/arj.ts:86

		return { bodyStart: offset + 4, bodySize: 0, nextOffset: offset + 4, metadataSize: 4, isEnd: true };
	if (bodySize < 30 || bodySize > ARJ_MAX_BASIC_HEADER) throw new ArchiveError("Invalid ARJ basic header size");
	const bodyStart = offset + 4;
	const bodyEnd = bodyStart + bodySize;
	assertRange(bytes, bodyStart, bodyEnd + 4, "basic header");
	if (crc32(bytes.subarray(bodyStart, bodyEnd)) !== u32(bytes, bodyEnd)) {
		throw new ArchiveError("Invalid ARJ basic header CRC32");
	}
	let cursor = bodyEnd + 4;
	let extensionCount = 0;
	for (;;) {
		assertRange(bytes, cursor, cursor + 2, "extended header size");
		const extensionSize = u16(bytes, cursor);
		cursor += 2;
		if (extensionSize === 0) break;
		if (++extensionCount > 65_535) throw new ArchiveError("Invalid ARJ archive: too many extended headers");
		assertRange(bytes, cursor, cursor + extensionSize + 4, "extended header");
		if (crc32(bytes.subarray(cursor, cursor + extensionSize)) !== u32(bytes, cursor + extensionSize)) {
			throw new ArchiveError("Invalid ARJ extended header CRC32");
		}
		cursor += extensionSize + 4;
		assertIndexSize(cursor - offset, options.limits, "header metadata");
	}
	return { bodyStart, bodySize, nextOffset: cursor, metadataSize: cursor - offset, isEnd: false };
}

class ArjBitReader {
	readonly #bytes: Uint8Array;
	#position = 0;

	constructor(bytes: Uint8Array) {
		this.#bytes = bytes;
	}

	read(count: number): number {
		if (!Number.isInteger(count) || count < 0 || count > 24 || this.#position + count > this.#bytes.byteLength * 8) {
			throw new ArchiveError("Invalid ARJ method-4 compressed data: truncated bitstream");

View on GitHub (pinned to 9690622007)

Solutions

  1. Test externally (`arj t` / 7-Zip) to confirm damage; restore the archive from backup or re-download.
  2. Identify which tool created/modified the file; re-create the archive with the original tool and default options.
  3. Re-pack the archive: extract what an external tolerant tool can, then rebuild it so all CRCs are recomputed.
  4. If writing ARJ files yourself, CRC-32 every extended-header payload and append it little-endian after the payload.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return readArj(data, options);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("extended header CRC32")) {
    // extended metadata is damaged; re-obtain or re-pack the archive
    throw new Error(`ARJ extended header corrupt in ${sourceName}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: During the extended-header walk in parseArjBlock: crc32(bytes[cursor..cursor+extensionSize]) differs from the u32 at cursor+extensionSize. Caused by byte corruption inside an extended header or a wrong extensionSize making the reader hash/verify the wrong region.

Common situations: Damaged downloads/media, archives modified by tools that rewrite extended headers (comments, timestamps, GOST/MD5 data) without recomputing their CRCs, or a corrupted chain where an earlier wrong size desynchronizes the CRC check of the next extension.

Related errors


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