{"record":{"id":"e2add6a5d095b0ff","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-lzx-block-produced-the-wrong","errorCode":null,"errorMessage":"Invalid CAB archive: LZX block produced the wrong byte count","messagePattern":"Invalid CAB archive: LZX block produced the wrong byte count","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzx.ts","lineNumber":211,"sourceCode":"\t\t}\n\t\tif (outputSize === 0) return new Uint8Array(0);\n\t\tconst reader = new LzxBitReader(bytes);\n\t\tif (!this.#headerRead) {\n\t\t\tif (reader.readBits(1) !== 0) {\n\t\t\t\tconst high = reader.readBits(16);\n\t\t\t\tconst low = reader.readBits(16);\n\t\t\t\tthis.#intelFileSize = signedUInt32((high * 0x10000 + low) >>> 0);\n\t\t\t}\n\t\t\tthis.#headerRead = true;\n\t\t}\n\n\t\tconst raw = new Uint8Array(outputSize);\n\t\tlet outputPosition = 0;\n\t\twhile (outputPosition < outputSize) {\n\t\t\tif (this.#blockRemaining === 0) this.#readBlockHeader(reader);\n\t\t\tconst run = Math.min(this.#blockRemaining, outputSize - outputPosition);\n\t\t\tconst produced = this.#decodeRun(reader, raw, outputPosition, run);\n\t\t\tif (produced !== run) throw new ArchiveError(\"Invalid CAB archive: LZX block produced the wrong byte count\");\n\t\t\toutputPosition += produced;\n\t\t\tthis.#blockRemaining -= produced;\n\t\t}\n\t\tif (this.#blockRemaining === 0 && this.#blockType === 3 && this.#uncompressedPadding) {\n\t\t\treader.readByte();\n\t\t\tthis.#uncompressedPadding = false;\n\t\t}\n\t\treader.alignWord();\n\n\t\tconst translated = this.#translateE8(raw);\n\t\tthis.#frame++;\n\t\treturn translated;\n\t}\n\n\t#readBlockHeader(reader: LzxBitReader): void {\n\t\tif (this.#blockType === 3 && this.#uncompressedPadding) reader.readByte();\n\t\tthis.#uncompressedPadding = false;\n\t\tthis.#blockType = reader.readBits(3);","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzx.ts#L193-L229","documentation":"#decodeRun must produce exactly the requested number of bytes (run); producing fewer means the LZX block ended early or its symbols decoded inconsistently with the declared block length. The decoder aborts rather than return a short, silently-corrupt buffer.","triggerScenarios":"A corrupt bitstream where a match/length sequence terminates before filling the requested run, or trees/offsets that cause the decode loop to end prematurely (e.g. after the 'match crosses boundary' guard would have fired in an inconsistent state).","commonSituations":"Corrupted CAB data, wrong block bytes passed to decompressFrame, streams from non-conforming encoders whose block length header disagrees with the encoded symbols.","solutions":["Verify CAB integrity and re-obtain the archive.","Ensure each decompressFrame call gets exactly one CFDATA block's compressed bytes in order.","Recompress with a standard CAB tool.","Create a fresh LzxDecoder per folder and never reuse one across folders or after a thrown error."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const out = decoder.decompressFrame(blockData, blockOutSize)\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('wrong byte count')) {\n    throw new Error('CAB archive corrupt: block decoded to fewer bytes than declared')\n  }\n  throw err\n}","preventionTips":["Verify CAB checksums before decompression","Feed exact CFDATA block bytes in order with a single decoder per folder","Never resume decoding with a decoder that previously threw"],"tags":["archive","cab","lzx","corruption","decoding"],"backgroundTag":"corrupt-archive-huffman-tree","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}