apache/iceberg · error · UncheckedIOException

UncheckedIOException wrapping IOException from bitmap…

Error message

UncheckedIOException wrapping IOException from bitmap deserialization

What it means

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.

Solutions

  1. 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
  2. Check writer/consumer library version compatibility for the bitmap encoding
  3. Catch UncheckedIOException at the read site and treat the deletion vector as unreadable rather than crashing the scan

Example fix

// before
RoaringBitmap bmp = RoaringPositionBitmap.bitmap(buffer);
// after
try { RoaringBitmap bmp = RoaringPositionBitmap.bitmap(buffer); }
catch (UncheckedIOException e) { throw new RuntimeException("Corrupt deletion vector bitmap", e); }
Defensive patterns

Strategy: try-catch

Validate before calling

// check blob integrity before deserializing
if (bytes == null || bytes.remaining() < 8) throw new IllegalStateException("Truncated bitmap blob");

Try / catch

try { bitmap = RoaringPositionBitmap.bitmap(buffer); } catch (UncheckedIOException e) { throw new IOException("Corrupt deletion vector blob", e.getCause()); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/b1c9932ae96e6066. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/deletes/RoaringPositionBitmap.java:317

    return (int) bitmapCount;
  }

  private static int readKey(ByteBuffer buffer, int lastKey) {
    int key = buffer.getInt();
    Preconditions.checkArgument(key >= 0, "Invalid unsigned key: %s", key);
    Preconditions.checkArgument(key <= Integer.MAX_VALUE - 1, "Key is too large: %s", key);
    Preconditions.checkArgument(key > lastKey, "Keys must be sorted in ascending order");
    return key;
  }

  private static RoaringBitmap readBitmap(ByteBuffer buffer) {
    try {
      RoaringBitmap bitmap = new RoaringBitmap();
      bitmap.deserialize(buffer);
      buffer.position(buffer.position() + bitmap.serializedSizeInBytes());
      return bitmap;
    } catch (IOException e) {
      throw new UncheckedIOException(e);
    }
  }

  // extracts high 32 bits from a 64-bit position (i.e. key)
  private static int key(long pos) {
    return (int) (pos >> 32);
  }

  // extracts low 32 bits from a 64-bit position (i.e. 32-bit position)
  private static int pos32Bits(long pos) {
    return (int) pos;
  }

  // combines high and low 32 bits into a 64-bit position
  // the low 32 bits must be bit-masked to avoid sign extension
  private static long toPosition(int key, int pos32Bits) {
    return (((long) key) << 32) | (((long) pos32Bits) & 0xFFFFFFFFL);
  }

View on GitHub (pinned to 86d9c8fc54)