can1357/oh-my-pi · error · ArchiveError

Invalid ARJ method-4 compressed data: non-zero trailing bits

Error message

Invalid ARJ method-4 compressed data: non-zero trailing bits

What it means

After decompression completes, method-4 streams are byte-aligned by convention: every remaining bit in the final partial byte should be 0 padding. assertZeroPadding walks those trailing bits and throws if any is 1, signaling the stream ended in an unexpected state — typically corruption or a decoder/encoder mismatch.

Source

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

	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");
		}
		let value = 0;
		for (let index = 0; index < count; index++) {
			const position = this.#position++;
			value = value * 2 + ((this.#bytes[position >>> 3]! >>> (7 - (position & 7))) & 1);
		}
		return value;
	}

	assertZeroPadding(): void {
		while (this.#position < this.#bytes.byteLength * 8) {
			if (this.read(1) !== 0) throw new ArchiveError("Invalid ARJ method-4 compressed data: non-zero trailing bits");
		}
	}
}

function decompressArjMethod4(packed: Uint8Array, outSize: number): Uint8Array {
	const reader = new ArjBitReader(packed);
	const output = new Uint8Array(outSize);
	let outputPosition = 0;
	while (outputPosition < outSize) {
		let lengthCode = 0;
		let lengthWidth = 0;
		for (; lengthWidth < 7; lengthWidth++) {
			if (reader.read(1) === 0) break;
			lengthCode += 2 ** lengthWidth;
		}
		if (lengthWidth !== 0) lengthCode += reader.read(lengthWidth);
		if (lengthCode === 0) {
			output[outputPosition++] = reader.read(8);

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify you sliced exactly compressedSize bytes of packed data — a one-byte overshoot imports the next member's bits and trips this check.
  2. Run `arj t` / 7-Zip on the archive; if it passes but this library fails, the file was made by a non-conformant encoder — re-pack with a standard tool.
  3. If the file is corrupt, restore from a trusted copy; padding-bit corruption usually accompanies wider damage.
  4. Do not modify the library to ignore trailing bits unless you accept silently tolerating corrupted tails; prefer strict re-verification.

Example fix

// before: rounding the slice length up to a byte boundary with padding included
const packed = bytes.subarray(dataStart, dataStart + Math.ceil(rawLen / 8) + 1);
// after: slice exactly the declared compressed size
const packed = bytes.subarray(dataStart, dataStart + entry.compressedSize);
Defensive patterns

Strategy: validation

Validate before calling

// Slice exactly the declared compressed size — overshooting imports neighboring padding bits
if (dataOffset + entry.compressedSize > bytes.byteLength) {
  throw new Error(`Member ${entry.name}: packed data exceeds buffer`);
}
const packed = bytes.subarray(dataOffset, dataOffset + entry.compressedSize);

Try / catch

try {
  return decompressArjMethod4(packed, originalSize);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("non-zero trailing bits")) {
    throw new Error(`Member ${name}: stream tail invalid — check packed slice length or re-obtain file`);
  }
  throw err;
}

Prevention

When it happens

Trigger: decompressArjMethod4() finishes emitting outSize bytes and calls assertZeroPadding(); a remaining bit is 1. Happens with corrupted packed tails, wrong compressed-size slices (padding bits of the next member included), or non-standard encoders that do not zero pad.

Common situations: An off-by-one compressedSize when extracting the packed payload so neighboring bytes leak in, bit-rot flipping a padding bit, or an old/buggy ARJ compressor whose output violates the zero-padding convention this reader enforces.

Related errors


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