{"record":{"id":"529317f09df7768b","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-truncated-lzx-data","errorCode":null,"errorMessage":"Invalid CAB archive: truncated LZX data","messagePattern":"Invalid CAB archive: truncated LZX data","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzx.ts","lineNumber":48,"sourceCode":"\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);\n\treadonly #firstCodes = new Uint32Array(17);\n\treadonly #firstSymbols = new Uint32Array(17);\n\treadonly #symbols: Uint16Array;","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzx.ts#L30-L66","documentation":"readByte() found the input exhausted while reading a raw byte (uncompressed block data, padding byte, or 32-bit repeated-offset values). Unlike the bitstream truncation error, this fires on byte-aligned reads: the buffer passed to decompressFrame() is shorter than the block headers and raw data inside it require.","triggerScenarios":"Calling decompressFrame with a CFDATA block containing a type-3 (uncompressed) block whose stored bytes extend past the buffer end: cbCompressed smaller than the actual block content, subarray cut mid-block, or a corrupt blockLength (up to 65535) that overruns the frame data.","commonSituations":"Truncated .cab downloads; CAB parsers that mis-handle uncompressed LZX blocks (they store 12 padding bits plus raw bytes, changing the length accounting); passing the wrong block's size fields to the decoder.","solutions":["Pass exactly cbCompressed bytes of the CFDATA block; verify cbCompressed is large enough for uncompressed blocks (padding + blockLength bytes).","Re-download/re-extract the CAB and validate with cabextract or 7z to rule out simple truncation.","Audit the caller's block iteration: each folder's cfdata blocks must be fed contiguously with correct sizes to one LzxDecoder instance.","If parsing CAB yourself, check for reserved-area/CFDATA offset handling errors that make you slice the wrong region."],"exampleFix":"// before\nconst block = data.subarray(offset, offset + 4096); // arbitrary chunk\nconst out = decoder.decompressFrame(block, 32768);\n// after\nconst size = cfdatum.cbCompressed;\nif (offset + size > data.byteLength) throw new Error(\"cab truncated\");\nconst out = decoder.decompressFrame(data.subarray(offset, offset + size), cfdatum.cbUncompressed);","handlingStrategy":"validation","validationCode":"const end = offset + cfdatum.cbCompressed;\nif (end > data.byteLength) throw new Error(\"CAB data truncated\");\n// uncompressed LZX blocks need padding + blockLength raw bytes inside cbCompressed\nif (cfdatum.cbCompressed < cfdatum.cbUncompressed) throw new Error(\"cbCompressed < cbUncompressed on LZX folder — parser bug\");","typeGuard":"function canReadBytes(data: Uint8Array, offset: number, count: number): boolean {\n  return count >= 0 && offset >= 0 && offset + count <= data.byteLength;\n}","tryCatchPattern":"try {\n  return decoder.decompressFrame(frameBytes, outputSize);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"truncated LZX data\")) {\n    throw new Error(\"Ran out of CFDATA bytes mid-block — CAB truncated or block sizes misparsed\");\n  }\n  throw e;\n}","preventionTips":["Respect cbCompressed for raw reads; uncompressed blocks still consume padding bits/bytes.","Validate the CAB with a reference extractor before feeding hand-parsed blocks to the decoder.","Detect truncation up front: every CFDATA offset+size must fit within the file."],"tags":["archive","cab","lzx","truncated-data"],"backgroundTag":"truncated-compressed-data","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}