can1357/oh-my-pi · error · ArchiveError

Invalid CAB archive: LZX match offset exceeds available hist

Error message

Invalid CAB archive: LZX match offset exceeds available history

What it means

The decoded match offset is zero or larger than the bytes decoded so far / window size, so the copy would read before the start of the output history. This is a definitive corruption signal in the LZX bitstream.

Source

Thrown at packages/utils/src/ar/codecs/lzx.ts:318

			} else {
				if (slot >= this.#positionBase.byteLength)
					throw new ArchiveError("Invalid CAB archive: LZX position slot is out of range");
				const extra = this.#extraBits[slot]!;
				matchOffset = this.#positionBase[slot]! - 2;
				if (this.#blockType === 2 && extra >= 3) {
					if (extra > 3) matchOffset += reader.readBits(extra - 3) * 8;
					if (!this.#alignedTable) throw new ArchiveError("Invalid CAB archive: missing LZX aligned tree");
					matchOffset += this.#alignedTable.decode(reader);
				} else if (extra !== 0) {
					matchOffset += reader.readBits(extra);
				}
				this.#r2 = this.#r1;
				this.#r1 = this.#r0;
				this.#r0 = matchOffset;
			}

			if (matchOffset <= 0 || matchOffset > Math.min(this.#decodedSize, this.#window.byteLength)) {
				throw new ArchiveError("Invalid CAB archive: LZX match offset exceeds available history");
			}
			for (let index = 0; index < matchLength; index++) {
				const source = (this.#windowPosition - matchOffset + this.#window.byteLength) % this.#window.byteLength;
				this.#writeByte(this.#window[source]!, output, outputStart + produced + index);
			}
			produced += matchLength;
		}
		return produced;
	}

	#writeByte(value: number, output: Uint8Array, outputPosition: number): void {
		output[outputPosition] = value;
		this.#window[this.#windowPosition] = value;
		this.#windowPosition = (this.#windowPosition + 1) % this.#window.byteLength;
		this.#decodedSize++;
	}

	#translateE8(raw: Uint8Array): Uint8Array {

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-obtain the CAB from a trusted source.
  2. Confirm you decompress frames in order from the first frame; LZX history depends on all prior output.
  3. Catch ArchiveError and report the file as corrupt.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  const out = decompressFrame(frame);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("exceeds available history")) {
    // corrupt back-reference — abort and report
  } else throw err;
}

Prevention

When it happens

Trigger: decompressFrame -> #decodeRun resolves a matchOffset <= 0 or > min(decodedSize, window.byteLength) before copying matchLength bytes from the ring window.

Common situations: Truncated or corrupted CAB extraction, wrong start offset into compressed data, fuzzed inputs.

Related errors


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