can1357/oh-my-pi · error · ArchiveError
Invalid LZH -lzs- data: match exceeds declared size
Error message
Invalid LZH -lzs- data: match exceeds declared size
What it means
In -lzs- mode, a match is an 11-bit history position plus 4-bit length; if the resulting length exceeds remaining output space, the library refuses to write past outSize.
Source
Thrown at packages/utils/src/ar/lzh.ts:265
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) {
if (reader.read(1) !== 0) {
emit(reader.read(8));
} else {
const position = reader.read(11);
const length = reader.read(4) + 2;
if (length > outSize - outputPosition)
throw new ArchiveError("Invalid LZH -lzs- data: match exceeds declared size");
for (let index = 0; index < length; index++) emit(history[(position + index) & 2047]!);
}
}
reader.assertZeroPadding();
return output;
}
const ZERO_CRC16_BYTES = new Uint8Array(2);
function crc16WithZeroRange(bytes: Uint8Array, zeroStart: number, zeroEnd: number): number {
if (zeroStart < 0) return crc16Arc(bytes);
let value = crc16Arc(bytes.subarray(0, zeroStart));
value = crc16Arc(ZERO_CRC16_BYTES.subarray(0, zeroEnd - zeroStart), value);
return crc16Arc(bytes.subarray(zeroEnd), value);
}
function u16(bytes: Uint8Array, offset: number): number {
return bytes[offset]! | (bytes[offset + 1]! << 8);View on GitHub (pinned to 9690622007)
Solutions
- Verify the member's declared size matches the data (check CRC)
- Re-obtain the archive
- Catch ArchiveError for untrusted input and skip the entry
Defensive patterns
Strategy: try-catch
Validate before calling
if (packed.length < 2) throw new Error("-lzs- stream too short");
if (!Number.isSafeInteger(outSize) || outSize <= 0) throw new Error("bad outSize"); Try / catch
try {
const out = decompressLzs(packed, outSize);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes("match exceeds")) return null;
throw err;
} Prevention
- Validate CRC of the source archive
- Confirm the declared size matches the member's real content
- Handle untrusted archives with ArchiveError-aware control flow
When it happens
Trigger: decompressLzs decodes a match whose length (4-bit+2) exceeds outSize - outputPosition — corrupt bitstream, or outSize smaller than the real uncompressed size.
Common situations: Wrong size field parsed from header; archives truncated/damaged in transit; fuzzed inputs.
Related errors
- Invalid LZH -lzs- data: output exceeds declared size
- Invalid tar octal value: ${value}
- Truncated embedded addon archive entry: ${filename}
- ARJ member '${memberPath}' has inconsistent stored size
- ARJ member '${memberPath}' has invalid no-data method sizes
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/fc1feb9d9deecc9b.
Report an issue: GitHub.