can1357/oh-my-pi · error · ArchiveError

Invalid ${label} compressed data: block exceeds declared siz

Error message

Invalid ${label} compressed data: block exceeds declared size

What it means

After the loop finishes (output full), a nonzero blockRemaining means the declared command count for the block was not fully consumed — the block claims more commands than the output size accounts for. The library treats this as corrupt data rather than silently accepting trailing references.

Source

Thrown at packages/utils/src/ar/lzh.ts:240

			continue;
		}
		const length = symbol - 256 + 3;
		if (length > outSize - outputPosition) {
			throw new ArchiveError(`Invalid ${label} compressed data: match exceeds declared size`);
		}
		const positionCode = positions!.decode(reader);
		let distance = positionCode;
		if (positionCode > 1) {
			const lowBitCount = positionCode - 1;
			distance = 2 ** lowBitCount + reader.read(lowBitCount);
		}
		if (distance >= dictionarySize || distance >= outputPosition) {
			throw new ArchiveError(`Invalid ${label} compressed data: history distance is out of range`);
		}
		let sourcePosition = outputPosition - distance - 1;
		for (let index = 0; index < length; index++) output[outputPosition++] = output[sourcePosition++]!;
	}
	if (blockRemaining !== 0) throw new ArchiveError(`Invalid ${label} compressed data: block exceeds declared size`);
	reader.assertZeroPadding();
	return output;
}

function decompressLzs(packed: Uint8Array, outSize: number): Uint8Array {
	const reader = new MsbBitReader(packed, "LZH -lzs-");
	const output = new Uint8Array(outSize);
	const history = new Uint8Array(2048);
	history.fill(0x20);
	let historyPosition = 2048 - 17;
	let outputPosition = 0;
	const emit = (value: number): void => {
		if (outputPosition >= outSize) throw new ArchiveError("Invalid LZH -lzs- data: output exceeds declared size");
		output[outputPosition++] = value;
		history[historyPosition] = value;
		historyPosition = (historyPosition + 1) & 2047;
	};
	while (outputPosition < outSize) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the original-size header field (including extended-header overrides)
  2. Check archive integrity (CRC) and re-obtain the file
  3. Treat the entry as corrupt: catch ArchiveError and skip/report
Defensive patterns

Strategy: try-catch

Validate before calling

if (!Number.isSafeInteger(outSize) || outSize <= 0) throw new Error("outSize must be a positive safe integer");

Try / catch

try {
  const out = decompressLhStatic(packed, outSize, positionBits, positionSymbols, label);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("block exceeds")) reportCorrupt(entry);
  else throw err;
}

Prevention

When it happens

Trigger: decompressLhStatic fills outSize exactly but the current block's 16-bit command counter is still nonzero — corrupt header count, wrong outSize (too small), or overlapping/malformed blocks.

Common situations: Archives whose original-size field was edited or damaged; streams where an earlier corrupt table caused misdecoding; fuzzed inputs.

Related errors


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