{"record":{"id":"234c2ae30b83ae87","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-misaligned-lzx-byte-stream","errorCode":null,"errorMessage":"Invalid CAB archive: misaligned LZX byte stream","messagePattern":"Invalid CAB archive: misaligned LZX byte stream","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzx.ts","lineNumber":45,"sourceCode":"\t\t\t\tthis.#word = this.#bytes[this.#offset]! | (this.#bytes[this.#offset + 1]! << 8);\n\t\t\t\tthis.#offset += 2;\n\t\t\t\tthis.#remaining = 16;\n\t\t\t}\n\t\t\tconst take = Math.min(needed, this.#remaining);\n\t\t\tvalue = value * 2 ** take + ((this.#word >>> (this.#remaining - take)) & (2 ** take - 1));\n\t\t\tthis.#remaining -= take;\n\t\t\tneeded -= take;\n\t\t}\n\t\treturn value;\n\t}\n\n\talignWord(): void {\n\t\tthis.#remaining = 0;\n\t}\n\n\treadByte(): number {\n\t\tif (this.#remaining !== 0) {\n\t\t\tthrow new ArchiveError(\"Invalid CAB archive: misaligned LZX byte stream\");\n\t\t}\n\t\tif (this.#offset >= this.#bytes.byteLength) {\n\t\t\tthrow new ArchiveError(\"Invalid CAB archive: truncated LZX data\");\n\t\t}\n\t\treturn this.#bytes[this.#offset++]!;\n\t}\n\n\treadUInt32LE(): number {\n\t\tconst b0 = this.readByte();\n\t\tconst b1 = this.readByte();\n\t\tconst b2 = this.readByte();\n\t\tconst b3 = this.readByte();\n\t\treturn (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)) >>> 0;\n\t}\n}\n\nclass LzxHuffmanTable {\n\treadonly #counts = new Uint32Array(17);","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzx.ts#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n// feeding two half-frames to the decoder\ndecoder.decompressFrame(data.subarray(0, 1000), 32768);\ndecoder.decompressFrame(data.subarray(500), 32768); // overlapping/desynced\n// after\nlet offset = 0;\nfor (const block of cfdataBlocks) {\n  const out = decoder.decompressFrame(data.subarray(offset, offset + block.cbCompressed), block.cbUncompressed);\n  offset += block.cbCompressed;\n}","handlingStrategy":"validation","validationCode":"// per folder: one decoder, frames in order, one frame's exact bytes per call\nif (folder.lzxDecoder === undefined) folder.lzxDecoder = new LzxDecoder(folder.windowBits);\nconst out = folder.lzxDecoder.decompressFrame(data.subarray(off, off + cbCompressed), cbUncompressed);","typeGuard":null,"tryCatchPattern":"try {\n  return decoder.decompressFrame(frameBytes, outputSize);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"misaligned LZX byte stream\")) {\n    throw new Error(\"LZX frame/block accounting desynced — verify frame order, sizes, and decoder-per-folder usage\");\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["archive","cab","lzx","bitstream-alignment","corrupt-data"],"backgroundTag":"bitstream-alignment-error","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}