{"record":{"id":"cefa55bb4ef8876e","repo":"can1357/oh-my-pi","slug":"invalid-xz-stream-truncated-filter-properties","errorCode":null,"errorMessage":"Invalid XZ stream: truncated filter properties","messagePattern":"Invalid XZ stream: truncated filter properties","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/xz.ts","lineNumber":447,"sourceCode":"\tif (offset >= bytes.byteLength || bytes[offset] === 0)\n\t\tthrow new ArchiveError(\"Invalid XZ stream: missing block header\");\n\tconst headerSize = (bytes[offset]! + 1) * 4;\n\tif (offset + headerSize > bytes.byteLength || headerSize < 8)\n\t\tthrow new ArchiveError(\"Invalid XZ stream: truncated block header\");\n\tif (crc32(bytes.subarray(offset, offset + headerSize - 4)) !== read32LE(bytes, offset + headerSize - 4))\n\t\tthrow new ArchiveError(\"Invalid XZ stream: block header CRC32 mismatch\");\n\tconst cursor: Cursor = { bytes, pos: offset + 1, limit: offset + headerSize - 4 };\n\tconst flags = bytes[cursor.pos++]!;\n\tif ((flags & 0x3c) !== 0) throw new ArchiveError(\"Unsupported XZ block flags\");\n\tconst filterCount = (flags & 3) + 1;\n\tconst declaredCompressed = (flags & 0x40) !== 0 ? readVarInt(cursor) : undefined;\n\tconst declaredUncompressed = (flags & 0x80) !== 0 ? readVarInt(cursor) : undefined;\n\tconst filters: XzFilter[] = [];\n\tfor (let index = 0; index < filterCount; index++) {\n\t\tconst id = readVarInt(cursor);\n\t\tconst propertySize = readVarInt(cursor);\n\t\tif (propertySize > cursor.limit - cursor.pos)\n\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\");","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/xz.ts#L429-L465","documentation":"A block header declares a filter whose properties blob (propertySize bytes) extends past the end of the header, so the declared property bytes are not present. The decoder throws rather than reading out of bounds. This indicates the block header is inconsistent or the stream is corrupt.","triggerScenarios":"xzDecompress encounters a block header where a filter's varint propertySize exceeds the bytes remaining between the cursor and the header's CRC field (cursor.limit - cursor.pos).","commonSituations":"Truncated or bit-corrupted .xz files, archives mangled by an intermediate transfer, or fuzzed/malicious inputs designed to make parsers misbehave.","solutions":["Re-acquire the archive and verify its integrity (checksum or `xz -t`).","Check that the byte buffer passed to xzDecompress is the complete file, not truncated by your own read logic.","If the file comes from an untrusted source, treat it as invalid input and reject it before decompression.","Re-compress the data with a standard `xz` encoder if the file came from a custom tool."],"exampleFix":"// before\nconst bytes = (await Bun.file('a.xz').arrayBuffer()).slice(0, 100); // accidental truncation\nawait xzDecompress(new Uint8Array(bytes), maxOutput);\n// after\nconst bytes = await Bun.file('a.xz').bytes(); // full file\nawait xzDecompress(bytes, maxOutput);","handlingStrategy":"validation","validationCode":"if (bytes.byteLength < 32) throw new Error('File too small to be a valid XZ stream');\nif (!isXz(bytes)) throw new Error('Missing XZ magic — refusing to decompress');","typeGuard":null,"tryCatchPattern":"try {\n\tconst out = await xzDecompress(bytes, maxOutput);\n} catch (err) {\n\tif (err instanceof ArchiveError && /truncated/i.test(err.message)) {\n\t\tthrow new Error(`Archive ${name} is corrupt or truncated; re-download it`);\n\t}\n\tthrow err;\n}","preventionTips":["Ensure file reads return the complete file (no accidental byte caps).","Validate downloads against published checksums before extraction.","Reject archives from untrusted sources that fail format checks.","Prefer standard xz-produced archives in your build pipeline."],"tags":["archive","xz","corrupt-data","truncated-input"],"backgroundTag":"xz-stream-corrupt","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}