{"record":{"id":"9e7de185d58ad864","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-cfdata-block-block-checksu","errorCode":null,"errorMessage":"Invalid CAB archive: CFDATA block ${block} checksum mismatch","messagePattern":"Invalid CAB archive: CFDATA block (.+?) checksum mismatch","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/cab.ts","lineNumber":162,"sourceCode":"\t\tfor (let block = 0; block < description.blockCount; block++) {\n\t\t\tif (position + DATA_BLOCK_SIZE + this.#dataReserveSize > bytes.byteLength) {\n\t\t\t\tthrow new ArchiveError(\"Invalid CAB archive: truncated CFDATA header\");\n\t\t\t}\n\t\t\tconst compressed = readUInt16LE(bytes, position + 4);\n\t\t\tconst uncompressed = readUInt16LE(bytes, position + 6);\n\t\t\tif (uncompressed === 0) throw new ArchiveError(\"Unsupported multi-volume CAB archive: split CFDATA block\");\n\t\t\tif (uncompressed > MAX_DATA_OUTPUT) {\n\t\t\t\tthrow new ArchiveError(`Invalid CAB archive: CFDATA expands to ${uncompressed} bytes (maximum 32768)`);\n\t\t\t}\n\t\t\tconst payloadStart = position + DATA_BLOCK_SIZE + this.#dataReserveSize;\n\t\t\tconst payloadEnd = payloadStart + compressed;\n\t\t\tif (payloadEnd > bytes.byteLength) throw new ArchiveError(\"Invalid CAB archive: truncated CFDATA payload\");\n\t\t\tconst expectedChecksum = readUInt32LE(bytes, position);\n\t\t\tif (expectedChecksum !== 0) {\n\t\t\t\tconst payloadChecksum = cabChecksum(bytes.subarray(payloadStart, payloadEnd));\n\t\t\t\tconst actualChecksum = cabChecksum(bytes.subarray(position + 4, payloadStart), payloadChecksum);\n\t\t\t\tif (actualChecksum !== expectedChecksum) {\n\t\t\t\t\tthrow new ArchiveError(`Invalid CAB archive: CFDATA block ${block} checksum mismatch`);\n\t\t\t\t}\n\t\t\t}\n\t\t\toutputSize += uncompressed;\n\t\t\tassertInMemorySize(outputSize, this.#limits);\n\t\t\tposition = payloadEnd;\n\t\t}\n\t\tif (outputSize < description.requiredSize) {\n\t\t\tthrow new ArchiveError(\"Invalid CAB archive: folder data is shorter than its file table declares\");\n\t\t}\n\n\t\tconst output = new Uint8Array(outputSize);\n\t\tconst lzx = description.method === 3 ? new LzxDecoder(description.parameter) : undefined;\n\t\tposition = 0;\n\t\tlet outputPosition = 0;\n\t\tfor (let block = 0; block < description.blockCount; block++) {\n\t\t\tconst compressed = readUInt16LE(bytes, position + 4);\n\t\t\tconst uncompressed = readUInt16LE(bytes, position + 6);\n\t\t\tconst payloadStart = position + DATA_BLOCK_SIZE + this.#dataReserveSize;","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/cab.ts#L144-L180","documentation":"Thrown by CabFolder.#decode when a CFDATA block's optional 32-bit checksum is present (non-zero) but does not match the XOR checksum computed over the block header (from offset+4 to payload start) and the compressed payload. The CAB format uses this checksum to detect bit-level corruption; a mismatch means the bytes on disk differ from what the cabinet writer produced.","triggerScenarios":"readCab() indexed a cabinet whose stored cfchecksum at the block start differs from cabChecksum(bytes[position+4..payloadStart], cabChecksum(payload)); any single-byte flip in header or payload of a checksummed block triggers it.","commonSituations":"Bit rot on old storage media; archives corrupted during transfer or by a faulty disk; files modified after creation; DOS/Windows-era cabinets with pre-existing damage.","solutions":["Re-obtain the archive from its original source and compare checksums","Test with `cabextract -t file.cab` to confirm corruption is in the file, not the reader","If you produce cabinets yourself, verify your writer computes the XOR checksum per the CAB spec (or stores 0 to skip checking)","If you must salvage data, use a repair-oriented extractor that skips bad blocks, accepting partial output"],"exampleFix":"// before\nawait extractCab(untrustedPath, dest);\n// after\nconst ok = await Bun.$`cabextract -t ${untrustedPath}`.quiet().nothrow();\nif (!ok.exitCode) throw new Error(`archive corrupt, refusing extraction: ${untrustedPath}`);\nawait extractCab(untrustedPath, dest);","handlingStrategy":"validation","validationCode":"const bytes = new Uint8Array(await Bun.file(path).arrayBuffer());\nif (!sniffCab(bytes.subarray(0, 4))) throw new Error('not a CAB file');\n// content integrity can only be checked by the reader; pre-verify whole-file hash instead\nif (await sha256(path) !== expectedHash) throw new Error('file hash mismatch — corrupted or modified');","typeGuard":null,"tryCatchPattern":"try {\n  await readCab(source, opts);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('checksum mismatch'))\n    logger.error('CAB block checksum failed; archive is corrupt', { path: archivePath });\n  throw err;\n}","preventionTips":["Store and compare whole-file hashes (sha256) for archived .cab artifacts","Keep archives on media/paths where they cannot be modified in place","Fail fast: treat checksum failure as corruption, never attempt partial extraction"],"tags":["archive","cab","checksum","corruption"],"backgroundTag":"checksum-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}