can1357/oh-my-pi · error · ArchiveError

Invalid LZMA2 stream: trailing data

Error message

Invalid LZMA2 stream: trailing data

What it means

After decoding the final LZMA2 chunk (marked by an end marker in the chunk control byte), the decoder requires the input cursor to land exactly at the end of the input buffer. If pos !== bytes.byteLength, extra undecoded bytes follow the terminated stream, so the input is not a single well-formed LZMA2 stream.

Source

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

				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. Trim trailing padding/zero bytes so the buffer ends exactly at the LZMA2 end marker.
  2. Split concatenated LZMA2 streams and decompress each separately.
  3. Extract the payload using the container's recorded stream length instead of reading a fixed block size.
  4. If the extra data is intentional, slice it off before calling the decompressor.

Example fix

// before: fixed 4096-byte read includes padding
const buf = new Uint8Array(4096);
lzmaDecompress(buf.subarray(0, bytesRead), max);
// after: use exact stream length
lzmaDecompress(buf.subarray(0, streamLength), max);
Defensive patterns

Strategy: try-catch

Validate before calling

if (bytes.length > 0 && bytes[bytes.length - 1] !== 0x00) {
  // LZMA2 streams end with a 0x00 terminator control byte
  throw new Error("Buffer does not end at an LZMA2 stream terminator");
}

Try / catch

try {
  return lzmaDecompress(bytes, maxOutput);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("trailing data")) {
    throw new Error("Extra bytes after LZMA2 end marker — split streams or trim padding", { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling lzmaDecompress on a buffer that contains an LZMA2 stream plus appended padding or an extra chunk after the terminating 0x00 control byte; concatenating multiple LZMA2 streams into one buffer and decoding them as one.

Common situations: Manually extracted .xz payload with trailing zero padding; concatenated archives; reading a fixed-size block from a file/stream that includes data beyond the stream end.

Related errors


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