{"record":{"id":"0010c36718e4ccd6","repo":"can1357/oh-my-pi","slug":"invalid-xz-stream-truncated-block-data","errorCode":null,"errorMessage":"Invalid XZ stream: truncated block data","messagePattern":"Invalid XZ stream: truncated block data","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/xz.ts","lineNumber":465,"sourceCode":"\t\t\tthrow new ArchiveError(\"Invalid XZ stream: truncated filter properties\");\n\t\tfilters.push({ id, properties: bytes.slice(cursor.pos, cursor.pos + propertySize) });\n\t\tcursor.pos += propertySize;\n\t}\n\twhile (cursor.pos < cursor.limit)\n\t\tif (bytes[cursor.pos++] !== 0) throw new ArchiveError(\"Invalid XZ stream: non-zero block header padding\");\n\tconst integritySize = checkSize(checkId);\n\tconst compressedSize = record.unpaddedSize - headerSize - integritySize;\n\tif (!Number.isSafeInteger(compressedSize) || compressedSize <= 0)\n\t\tthrow new ArchiveError(\"Invalid XZ stream: compressed block size is invalid\");\n\tif (declaredCompressed !== undefined && declaredCompressed !== compressedSize)\n\t\tthrow new ArchiveError(\"Invalid XZ stream: block compressed size mismatch\");\n\tif (declaredUncompressed !== undefined && declaredUncompressed !== record.uncompressedSize)\n\t\tthrow new ArchiveError(\"Invalid XZ stream: block uncompressed size mismatch\");\n\tconst compressedStart = offset + headerSize;\n\tconst compressedEnd = compressedStart + compressedSize;\n\tconst paddingSize = (4 - ((headerSize + compressedSize) & 3)) & 3;\n\tconst checkStart = compressedEnd + paddingSize;\n\tif (checkStart + integritySize > bytes.byteLength) throw new ArchiveError(\"Invalid XZ stream: truncated block data\");\n\tconst last = filters[filters.length - 1]!;\n\tif (last.id !== 0x21 || last.properties.byteLength !== 1)\n\t\tthrow new ArchiveError(`Unsupported XZ terminal filter ID 0x${last.id.toString(16)} (LZMA2 required)`);\n\tlet output = await lzma2Decompress(\n\t\tlast.properties[0]!,\n\t\tbytes.subarray(compressedStart, compressedEnd),\n\t\trecord.uncompressedSize,\n\t);\n\tif (output.byteLength !== record.uncompressedSize)\n\t\tthrow new ArchiveError(\"Invalid XZ stream: decoded block size mismatch\");\n\toutput = output.slice();\n\tfor (let index = filters.length - 2; index >= 0; index--) applyFilter(output, filters[index]!);\n\tfor (let index = compressedEnd; index < checkStart; index++)\n\t\tif (bytes[index] !== 0) throw new ArchiveError(\"Invalid XZ stream: non-zero block padding\");\n\tverifyCheck(checkId, output, bytes.subarray(checkStart, checkStart + integritySize));\n\tconst paddedEnd = offset + Math.ceil(record.unpaddedSize / 4) * 4;\n\tif (checkStart + integritySize !== paddedEnd)\n\t\tthrow new ArchiveError(\"Invalid XZ stream: block size does not match its index record\");","sourceCodeStart":447,"sourceCodeEnd":483,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/xz.ts#L447-L483","documentation":"The decoder computed where the block's integrity check should live (compressedEnd + 4-byte alignment padding) and it extends past the end of the input buffer. The stream is physically truncated — the bytes the index promised are not there.","triggerScenarios":"xzDecompress is given a buffer where checkStart + integritySize exceeds bytes.byteLength for some block — typically a partially downloaded or prematurely truncated .xz file.","commonSituations":"Interrupted downloads, files copied with size limits (e.g. partial rsync/scp), reading a growing log archive mid-write, or a truncated upload.","solutions":["Re-download the complete file and compare its size/checksum against the source.","Check the code path that reads the file — ensure you read the whole file (Bun.file().bytes()/arrayBuffer()) without a byte cap.","If the archive is being written concurrently, wait for the writer to finish before decompressing.","Catch ArchiveError and surface a 'file truncated/incomplete' message to users."],"exampleFix":"// before\nconst buf = new Uint8Array(await file.arrayBuffer(), 0, 1_000_000); // capped slice\nawait xzDecompress(buf, maxOutput);\n// after\nconst buf = await file.bytes(); // full contents\nawait xzDecompress(buf, maxOutput);","handlingStrategy":"validation","validationCode":"const stat = await fs.stat(path);\nif (expectedSize !== undefined && stat.size < expectedSize) {\n\tthrow new Error(`Archive incomplete: ${stat.size}/${expectedSize} bytes`);\n}\nconst bytes = await Bun.file(path).bytes();\nif (!isXz(bytes)) throw new Error('Not an XZ stream');","typeGuard":null,"tryCatchPattern":"try {\n\treturn await xzDecompress(bytes, maxOutput);\n} catch (err) {\n\tif (err instanceof ArchiveError && /truncated/i.test(err.message)) {\n\t\tthrow new Error(`Download incomplete — re-fetch ${path}`);\n\t}\n\tthrow err;\n}","preventionTips":["Compare file size against the published size before extraction.","Retry interrupted downloads until the checksum matches.","Do not decompress archives while a writer is still producing them.","Read files fully via Bun.file().bytes() instead of capped slices."],"tags":["archive","xz","truncated-input","corrupt-data"],"backgroundTag":"archive-truncated","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}