can1357/oh-my-pi · error · ArchiveError

Invalid LZMA2 chunk size

Error message

Invalid LZMA2 chunk size

What it means

LZMA2 chunk headers declare packed and unpacked sizes. This error is thrown when the chunk header's packSize exceeds the remaining input bytes, or its unpackSize exceeds the remaining decoder output capacity (maxOutput minus already-written output). It guards against buffers that lie about their own chunk framing.

Source

Thrown at packages/utils/src/ar/codecs/lzma.ts:424

			const resetsState = control >= 0xa0;
			const setsProperties = control >= 0xc0;
			if (needDictionaryReset && !resetsDictionary)
				throw new ArchiveError("Invalid LZMA2 stream: dictionary was not initialized");
			if (!propertiesSet && !setsProperties)
				throw new ArchiveError("Invalid LZMA2 stream: properties were not initialized");
			if (setsProperties) {
				let property = takeByte();
				if (property >= 9 * 5 * 5) throw new ArchiveError("Invalid LZMA2 properties");
				const lc = property % 9;
				property = Math.floor(property / 9);
				const lp = property % 5;
				const pb = Math.floor(property / 5);
				if (lc + lp > 4) throw new ArchiveError("Invalid LZMA2 literal properties");
				decoder.setProperties(lc, lp, pb);
				propertiesSet = true;
			}
			if (packSize > bytes.byteLength - pos || unpackSize > maxOutput - decoder.outputPos)
				throw new ArchiveError("Invalid LZMA2 chunk size");
			if (resetsDictionary) decoder.resetDictionary();
			else if (resetsState) decoder.resetState();
			decoder.decodeChunk(bytes.subarray(pos, pos + packSize), unpackSize);
			pos += packSize;
			needDictionaryReset = false;
		}
		if (pos !== bytes.byteLength) throw new ArchiveError("Invalid LZMA2 stream: trailing data");
		return output.subarray(0, decoder.outputPos);
	} catch (error) {
		if (error instanceof ArchiveError) throw error;
		throw new ArchiveError(`Invalid LZMA2 stream: ${error instanceof Error ? error.message : String(error)}`);
	}
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Increase the maxOutput parameter to at least the expected decompressed size.
  2. Pass the complete LZMA2 stream bytes — verify the input wasn't truncated (compare against source size/checksum).
  3. Validate the container (.xz) integrity before extracting the LZMA2 payload.
  4. Ensure you're not resuming mid-stream without preserving decoder state; start from the stream beginning.

Example fix

// before
lzmaDecompress(bytes, 1024);
// after: size the output to the file's recorded uncompressed size
lzmaDecompress(bytes, recordedUncompressedSize);
Defensive patterns

Strategy: validation

Validate before calling

if (!Number.isSafeInteger(maxOutput) || maxOutput <= 0) throw new Error("maxOutput must be a positive integer");
if (bytes.byteLength === 0) throw new Error("Empty LZMA2 payload");

Try / catch

try {
  return lzmaDecompress(bytes, maxOutput);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("chunk size")) {
    throw new Error("LZMA2 payload truncated or maxOutput too small — check stream completeness and output budget", { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling lzmaDecompress (decodeLzma2) with a truncated input buffer whose last chunk header promises more packed bytes than remain, or an unpackSize larger than the remaining maxOutput budget.

Common situations: Truncated .xz/.lzma downloads; passing a subarray that cuts off mid-chunk; calling with a maxOutput smaller than the actual decompressed size; corrupted archive payload.

Related errors


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