{"record":{"id":"09636e87e6060413","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-lzx-frame-size-outputsize","errorCode":null,"errorMessage":"Invalid CAB archive: LZX frame size ${outputSize} exceeds 32768 bytes","messagePattern":"Invalid CAB archive: LZX frame size (.+?) exceeds 32768 bytes","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/lzx.ts","lineNumber":192,"sourceCode":"\tconstructor(windowBits: number) {\n\t\tif (!Number.isInteger(windowBits) || windowBits < 15 || windowBits > 21) {\n\t\t\tthrow new ArchiveError(`Unsupported CAB LZX window size: ${windowBits} bits (expected 15-21)`);\n\t\t}\n\t\tthis.#window = new Uint8Array(2 ** windowBits);\n\t\tconst slots = POSITION_SLOTS[windowBits - 15]!;\n\t\tthis.#mainLengths = new Uint8Array(256 + slots * 8);\n\t\tthis.#extraBits = new Uint8Array(slots);\n\t\tthis.#positionBase = new Uint32Array(slots);\n\t\tfor (let slot = 0; slot < slots; slot++) {\n\t\t\tthis.#extraBits[slot] = slot < 4 ? 0 : Math.min(17, Math.floor(slot / 2) - 1);\n\t\t\tif (slot > 0) this.#positionBase[slot] = this.#positionBase[slot - 1]! + 2 ** this.#extraBits[slot - 1]!;\n\t\t}\n\t}\n\n\t/** Decode one CAB CFDATA LZX frame while preserving the folder's dictionary and Huffman state. */\n\tdecompressFrame(bytes: Uint8Array, outputSize: number): Uint8Array {\n\t\tif (!Number.isInteger(outputSize) || outputSize < 0 || outputSize > FRAME_SIZE) {\n\t\t\tthrow new ArchiveError(`Invalid CAB archive: LZX frame size ${outputSize} exceeds 32768 bytes`);\n\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);","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/lzx.ts#L174-L210","documentation":"CAB LZX frames decode at most FRAME_SIZE (32768) bytes each; decompressFrame validates outputSize is a non-negative integer within that bound before decoding. A larger request means the caller split frames incorrectly or the folder metadata is corrupt.","triggerScenarios":"decoder.decompressFrame(bytes, 40000), passing a negative or non-integer outputSize, or summing multiple CFDATA blocks' uncompressed sizes into one call instead of one call per block.","commonSituations":"Misreading CFDATA cbUncompressed (which can exceed 32768 only across folder boundaries), feeding an entire folder's data to a single frame call, integer parse errors from archive metadata.","solutions":["Call decompressFrame once per CFDATA block with that block's own uncompressed size (max 32768).","Split larger outputs into successive 32 KiB frames, reusing the same LzxDecoder to preserve dictionary state.","Validate outputSize with Number.isInteger(size) && size >= 0 && size <= 32768 before calling.","Sanity-check the folder's block sizes against the CAB header; nonsensical sizes indicate corruption."],"exampleFix":"// before\ndecoder.decompressFrame(block.data, folder.totalUncompressedSize)\n// after\ndecoder.decompressFrame(block.data, block.uncompressedSize) // one CFDATA block per call, ≤ 32768","handlingStrategy":"validation","validationCode":"function assertValidFrameSize(outputSize: number): void {\n  if (!Number.isInteger(outputSize) || outputSize < 0 || outputSize > 32768) {\n    throw new Error(`Frame output size must be an integer in [0, 32768], got ${outputSize}`)\n  }\n}\nassertValidFrameSize(block.uncompressedSize)","typeGuard":"function isValidFrameSize(v: unknown): v is number {\n  return typeof v === 'number' && Number.isInteger(v) && v >= 0 && v <= 32768\n}","tryCatchPattern":"try {\n  return decoder.decompressFrame(data, outputSize)\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('frame size')) {\n    throw new Error('Frame split error: call decompressFrame once per CFDATA block (≤32768 bytes)')\n  }\n  throw err\n}","preventionTips":["One decompressFrame call per CFDATA block, using that block's own uncompressed size","Reuse the decoder across frames of the same folder to preserve window state","Cross-check block sizes against the CAB folder header for corruption"],"tags":["archive","cab","lzx","validation","frame-size"],"backgroundTag":"invalid-parameter-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}