can1357/oh-my-pi · error · ArchiveError
Invalid CAB archive: misaligned LZX byte stream
Error message
Invalid CAB archive: misaligned LZX byte stream
What it means
LZX interleaves bit-level (Huffman) and byte-level (uncompressed block) reads; byte reads are only valid when the bit reader is word-aligned (no buffered bits). This error means readByte() was called while bits remained in the current 16-bit word — the decoder attempted to read raw bytes for an uncompressed block (type 3) or padding without aligning first, indicating a corrupt/misdeserialized stream.
Source
Thrown at packages/utils/src/ar/codecs/lzx.ts:45
this.#word = this.#bytes[this.#offset]! | (this.#bytes[this.#offset + 1]! << 8);
this.#offset += 2;
this.#remaining = 16;
}
const take = Math.min(needed, this.#remaining);
value = value * 2 ** take + ((this.#word >>> (this.#remaining - take)) & (2 ** take - 1));
this.#remaining -= take;
needed -= take;
}
return value;
}
alignWord(): void {
this.#remaining = 0;
}
readByte(): number {
if (this.#remaining !== 0) {
throw new ArchiveError("Invalid CAB archive: misaligned LZX byte stream");
}
if (this.#offset >= this.#bytes.byteLength) {
throw new ArchiveError("Invalid CAB archive: truncated LZX data");
}
return this.#bytes[this.#offset++]!;
}
readUInt32LE(): number {
const b0 = this.readByte();
const b1 = this.readByte();
const b2 = this.readByte();
const b3 = this.readByte();
return (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)) >>> 0;
}
}
class LzxHuffmanTable {
readonly #counts = new Uint32Array(17);View on GitHub (pinned to 9690622007)
Solutions
- Ensure each decompressFrame() call receives exactly one frame's compressed data in archive order, on a single LzxDecoder per folder.
- Verify the CAB block stream integrity — the preceding compressed block length probably disagrees with its header; re-extract the archive.
- If writing a custom parser, honor LZX framing: block headers, blockLength, and the unconditional alignWord() at end of frame must all be respected.
- Test the same CAB with a reference tool (7z, cabextract) to confirm the archive itself is valid before debugging the decoder usage.
Example fix
// before
// feeding two half-frames to the decoder
decoder.decompressFrame(data.subarray(0, 1000), 32768);
decoder.decompressFrame(data.subarray(500), 32768); // overlapping/desynced
// after
let offset = 0;
for (const block of cfdataBlocks) {
const out = decoder.decompressFrame(data.subarray(offset, offset + block.cbCompressed), block.cbUncompressed);
offset += block.cbCompressed;
} Defensive patterns
Strategy: validation
Validate before calling
// per folder: one decoder, frames in order, one frame's exact bytes per call if (folder.lzxDecoder === undefined) folder.lzxDecoder = new LzxDecoder(folder.windowBits); const out = folder.lzxDecoder.decompressFrame(data.subarray(off, off + cbCompressed), cbUncompressed);
Try / catch
try {
return decoder.decompressFrame(frameBytes, outputSize);
} catch (e) {
if (e instanceof Error && e.message.includes("misaligned LZX byte stream")) {
throw new Error("LZX frame/block accounting desynced — verify frame order, sizes, and decoder-per-folder usage");
}
throw e;
} Prevention
- Use exactly one LzxDecoder instance per CAB folder, in archive order — state carries across 32 KiB frames.
- Feed each decompressFrame() call one frame's complete compressed data, never overlapping subarrays.
- Cross-check suspicious archives with cabextract/7z to separate file corruption from caller bugs.
When it happens
Trigger: A block-type-3 (uncompressed) header or its padding byte read while the bit reader still holds unconsumed bits: the preceding block's bitstream length doesn't match what the code computed (corrupt blockLength), or the caller's outputSize/frame accounting desynced the block stream.
Common situations: Corrupt CAB archives where a compressed block's declared length is wrong; hand-rolled CAB extraction feeding frames in the wrong order or skipping alignWord() boundaries; mixing frames from different folders on one decoder instance.
Related errors
- Invalid CAB archive: invalid LZX Huffman code length
- Invalid CAB archive: invalid LZX repeated offset
- Invalid CAB archive: unsupported LZX block type ${this.#bloc
- Invalid CAB archive: LZX match crosses a frame or block boun
- Invalid CAB archive: LZX position slot is out of range
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/234c2ae30b83ae87.
Report an issue: GitHub.