{"record":{"id":"eb97a419405c4f3d","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-truncated-lzx-bitstream","errorCode":null,"errorMessage":"Invalid CAB archive: truncated LZX bitstream","messagePattern":"Invalid CAB archive: truncated LZX bitstream","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzx.ts","lineNumber":25,"sourceCode":"const POSITION_SLOTS = [30, 32, 34, 36, 38, 42, 50] as const;\n\nclass LzxBitReader {\n\treadonly #bytes: Uint8Array;\n\t#offset = 0;\n\t#word = 0;\n\t#remaining = 0;\n\n\tconstructor(bytes: Uint8Array) {\n\t\tthis.#bytes = bytes;\n\t}\n\n\treadBits(count: number): number {\n\t\tlet value = 0;\n\t\tlet needed = count;\n\t\twhile (needed > 0) {\n\t\t\tif (this.#remaining === 0) {\n\t\t\t\tif (this.#offset + 2 > this.#bytes.byteLength) {\n\t\t\t\t\tthrow new ArchiveError(\"Invalid CAB archive: truncated LZX bitstream\");\n\t\t\t\t}\n\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 {","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzx.ts#L7-L43","documentation":"The LZX bit reader refills from the CAB CFDATA block two bytes at a time; this error means it needed more bits but the input buffer ran out. The compressed data block is truncated relative to what the stream's decoded output requires — either the CFDATA block was cut short or the caller passed fewer bytes than the block header promises (cbCompressed).","triggerScenarios":"Calling LzxDecoder.decompressFrame(bytes, outputSize) with a bytes buffer shorter than the frame's compressed size: reading CFDATA blocks with a wrong cbCompressed value, slicing the archive incorrectly, or a corrupt CAB whose data block length field doesn't match actual stored bytes.","commonSituations":"Partial downloads of .cab files; custom CAB parsers passing only part of a multi-block folder's data; corrupted archives; using the LZX decoder directly with per-block buffers when the caller accumulated the wrong byte counts.","solutions":["Pass the full CFDATA block bytes for the frame — exactly cbCompressed bytes as declared in the CAB header, read contiguously.","Verify the CAB file is complete (file size vs. total declared blocks) and re-download if truncated.","If the folder spans multiple data blocks, concatenate/reserve the folder's compressed data before decoding frames rather than feeding partial buffers.","Validate your CAB parser's cbCompressed/cbUncompressed accounting against the remaining file length."],"exampleFix":"// before\nconst frame = cabData.subarray(0, guessed); // guessed too small\nconst out = decoder.decompressFrame(frame, 32768);\n// after\nconst frame = cabData.subarray(offset, offset + cfdatum.cbCompressed);\nconst out = decoder.decompressFrame(frame, cfdatum.cbUncompressed);","handlingStrategy":"validation","validationCode":"if (cfdatum.cbCompressed > data.byteLength - offset) {\n  throw new Error(`CAB truncated: need ${cfdatum.cbCompressed} bytes at ${offset}, have ${data.byteLength - offset}`);\n}\nconst frame = data.subarray(offset, offset + cfdatum.cbCompressed);","typeGuard":"function hasFullCfdataBlock(data: Uint8Array, offset: number, cbCompressed: number): boolean {\n  return offset >= 0 && cbCompressed >= 0 && offset + cbCompressed <= data.byteLength;\n}","tryCatchPattern":"try {\n  return decoder.decompressFrame(frame, outputSize);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"truncated LZX bitstream\")) {\n    throw new Error(\"CFDATA block shorter than declared — CAB file is truncated or misparsed\");\n  }\n  throw e;\n}","preventionTips":["Slice frames using the header-declared cbCompressed, never guessed chunk sizes.","Check total CAB file size against summed block sizes before extraction.","Verify partial downloads complete (Content-Length vs actual bytes) before parsing."],"tags":["archive","cab","lzx","truncated-data","bitstream"],"backgroundTag":"truncated-compressed-data","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}