{"record":{"id":"199cc343ff687f65","repo":"can1357/oh-my-pi","slug":"bzip2-block-exceeds-its-declared-block-size-level","errorCode":null,"errorMessage":"Bzip2 block exceeds its declared block-size level","messagePattern":"Bzip2 block exceeds its declared block-size level","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/codecs/bzip2.ts","lineNumber":293,"sourceCode":"\tlet selectorIndex = 0;\n\tlet groupRemaining = 0;\n\tlet table: HuffmanTable | undefined;\n\n\tconst nextSymbol = (): number => {\n\t\tif (groupRemaining === 0) {\n\t\t\tif (selectorIndex >= selectors.length) {\n\t\t\t\tthrow new ArchiveError(\"Bzip2 block exhausted its Huffman selectors\");\n\t\t\t}\n\t\t\ttable = tables[selectors[selectorIndex++]!];\n\t\t\tgroupRemaining = GROUP_SIZE;\n\t\t}\n\t\tgroupRemaining--;\n\t\treturn table!.decode(reader);\n\t};\n\n\tconst append = (byte: number, count: number): void => {\n\t\tif (count < 0 || blockLength + count > blockSizeLimit) {\n\t\t\tthrow new ArchiveError(\"Bzip2 block exceeds its declared block-size level\");\n\t\t}\n\t\tblock.fill(byte, blockLength, blockLength + count);\n\t\tblockLength += count;\n\t};\n\n\tconst endSymbol = usedBytes.length + 1;\n\tlet symbol = nextSymbol();\n\twhile (symbol !== endSymbol) {\n\t\tif (symbol === 0 || symbol === 1) {\n\t\t\tlet runLength = 0;\n\t\t\tlet power = 1;\n\t\t\tdo {\n\t\t\t\trunLength += symbol === 0 ? power : power * 2;\n\t\t\t\tif (runLength > blockSizeLimit || power > blockSizeLimit) {\n\t\t\t\t\tthrow new ArchiveError(\"Invalid bzip2 RLE run length\");\n\t\t\t\t}\n\t\t\t\tpower *= 2;\n\t\t\t\tsymbol = nextSymbol();","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/codecs/bzip2.ts#L275-L311","documentation":"bzip2 blocks declare a block-size level (100KB–900KB). The decoder enforces that RLE2 'append(byte, count)' never pushes total block output past that declared limit. This error fires when a decoded BWT/RLE run length would overflow the declared block size — the stream contradicts its own header.","triggerScenarios":"Calling append (from decodeBlockData) with a computed run count that is negative (corrupt RUNA/RUNB sequence) or pushes blockLength beyond blockSizeLimit, i.e. a corrupt bzip2 block or a mismatched blockSizeLimit derived from the stream header ('BZh9' etc.).","commonSituations":"Decoding damaged .bz2 data; a stream whose header level digit was altered; feeding bytes from one block into a decoder initialized for another; fuzzing/maliciously crafted archives.","solutions":["Test the archive with `bzip2 -t` and replace the corrupted file.","Confirm the input passed to the decompressor starts at the 'BZh' magic — an offset stream misaligns the header and block sizes.","If concatenating/dealing with multi-stream bz2, split on the proper stream end marker rather than arbitrary offsets.","If this happens on trusted data, report/log the byte offset; this indicates an encoder or library bug."],"exampleFix":"// before: offset slice skipping the BZh header\nconst body = bytes.subarray(4);\ndecompressBzip2(body);\n// after: include the full stream with its header\ndecompressBzip2(bytes);","handlingStrategy":"try-catch","validationCode":"const header = new TextDecoder().decode(bytes.subarray(0, 3));\nif (header !== \"BZh\" || !(bytes[3]! >= 0x31 && bytes[3]! <= 0x39)) throw new Error(\"Not a bzip2 stream\");","typeGuard":null,"tryCatchPattern":"try {\n  return decompressBzip2(bytes);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"block-size\")) {\n    throw new Error(\"bzip2 block data contradicts its header — archive is corrupt\", { cause: err });\n  }\n  throw err;\n}","preventionTips":["Verify the 'BZh<1-9>' header before decompressing so blockSizeLimit is derived from a real header.","Never feed bytes from one stream/block into a decoder initialized for another.","Run `bzip2 -t` on untrusted or transferred archives.","Treat mismatches on trusted data as a library/encoder bug and report with the byte offset."],"tags":["archive","bzip2","corrupt-data"],"backgroundTag":"corrupt-archive-bitstream","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}