{"record":{"id":"b1c9932ae96e6066","repo":"apache/iceberg","slug":"uncheckedioexception-wrapping-ioexception-from-bit","errorCode":null,"errorMessage":"UncheckedIOException wrapping IOException from bitmap deserialization","messagePattern":"UncheckedIOException wrapping IOException from bitmap deserialization","errorType":"exception","errorClass":"UncheckedIOException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/deletes/RoaringPositionBitmap.java","lineNumber":317,"sourceCode":"    return (int) bitmapCount;\n  }\n\n  private static int readKey(ByteBuffer buffer, int lastKey) {\n    int key = buffer.getInt();\n    Preconditions.checkArgument(key >= 0, \"Invalid unsigned key: %s\", key);\n    Preconditions.checkArgument(key <= Integer.MAX_VALUE - 1, \"Key is too large: %s\", key);\n    Preconditions.checkArgument(key > lastKey, \"Keys must be sorted in ascending order\");\n    return key;\n  }\n\n  private static RoaringBitmap readBitmap(ByteBuffer buffer) {\n    try {\n      RoaringBitmap bitmap = new RoaringBitmap();\n      bitmap.deserialize(buffer);\n      buffer.position(buffer.position() + bitmap.serializedSizeInBytes());\n      return bitmap;\n    } catch (IOException e) {\n      throw new UncheckedIOException(e);\n    }\n  }\n\n  // extracts high 32 bits from a 64-bit position (i.e. key)\n  private static int key(long pos) {\n    return (int) (pos >> 32);\n  }\n\n  // extracts low 32 bits from a 64-bit position (i.e. 32-bit position)\n  private static int pos32Bits(long pos) {\n    return (int) pos;\n  }\n\n  // combines high and low 32 bits into a 64-bit position\n  // the low 32 bits must be bit-masked to avoid sign extension\n  private static long toPosition(int key, int pos32Bits) {\n    return (((long) key) << 32) | (((long) pos32Bits) & 0xFFFFFFFFL);\n  }","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/deletes/RoaringPositionBitmap.java#L299-L335","documentation":"RoaringPositionBitmap.readBitmap deserializes a RoaringBitmap from a ByteBuffer. bitmap.deserialize throws IOException if the buffer is not a valid bitmap encoding; the method wraps it in UncheckedIOException so it can propagate from non-IO signature contexts. This indicates corrupt or malformed serialized bitmap data.","triggerScenarios":"Deserializing a position bitmap from bytes that are not a valid RoaringBitmap encoding — truncated buffer, wrong offset, corrupted blob (e.g. a damaged Puffin/deletion-vector blob), or bytes produced by an incompatible version.","commonSituations":"Reading deletion-vector blobs written by another writer/version, hand-edited or truncated files, or passing a buffer positioned at the wrong offset into the serialization.","solutions":["Verify the blob bytes and offset are the exact serialized RoaringBitmap (not truncated or shifted); regenerate the deletion vector from source delete files if corrupt","Check writer/consumer library version compatibility for the bitmap encoding","Catch UncheckedIOException at the read site and treat the deletion vector as unreadable rather than crashing the scan"],"exampleFix":"// before\nRoaringBitmap bmp = RoaringPositionBitmap.bitmap(buffer);\n// after\ntry { RoaringBitmap bmp = RoaringPositionBitmap.bitmap(buffer); }\ncatch (UncheckedIOException e) { throw new RuntimeException(\"Corrupt deletion vector bitmap\", e); }","handlingStrategy":"try-catch","validationCode":"// check blob integrity before deserializing\nif (bytes == null || bytes.remaining() < 8) throw new IllegalStateException(\"Truncated bitmap blob\");","typeGuard":null,"tryCatchPattern":"try { bitmap = RoaringPositionBitmap.bitmap(buffer); } catch (UncheckedIOException e) { throw new IOException(\"Corrupt deletion vector blob\", e.getCause()); }","preventionTips":["Validate blob length/offset before deserializing","Round-trip serialize/deserialize in tests after any writer change","Treat UncheckedIOException here as data corruption, and re-generate DVs rather than retrying the same bytes"],"tags":["java","serialization","corrupt-data"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}