apache/iceberg · error · EOFException

Invalid position: ${newPos} > stream length, ${plainStreamSi

Error message

Invalid position: ${newPos} > stream length, ${plainStreamSize}

What it means

AesGcmInputStream.seek() throws EOFException when the requested position newPos exceeds plainStreamSize, the total plaintext length of the encrypted stream. The message reports both the requested position and the stream length. Seeking past the end is not allowed for this stream.

Source

Thrown at core/src/main/java/org/apache/iceberg/encryption/AesGcmInputStream.java:153

        this.plainStreamPosition += bytesToCopy;
      } else if (available() > 0) {
        decryptBlock(blockIndex(plainStreamPosition));

      } else {
        break;
      }
    }

    // return -1 for EOF
    return totalBytesRead > 0 ? totalBytesRead : -1;
  }

  @Override
  public void seek(long newPos) throws IOException {
    if (newPos < 0) {
      throw new IOException("Invalid position: " + newPos);
    } else if (newPos > plainStreamSize) {
      throw new EOFException(
          "Invalid position: " + newPos + " > stream length, " + plainStreamSize);
    }

    this.plainStreamPosition = newPos;
  }

  @Override
  public long skip(long n) {
    if (n <= 0) {
      return 0;
    }

    long bytesLeftInStream = plainStreamSize - plainStreamPosition;
    if (n > bytesLeftInStream) {
      // skip the rest of the stream
      this.plainStreamPosition = plainStreamSize;
      return bytesLeftInStream;
    }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Re-read file length/metadata so seek targets match the actual stream size
  2. Check whether the underlying encrypted file was truncated or replaced; re-fetch the correct file
  3. Validate newPos against plainStreamSize before seeking and handle gracefully

Example fix

// before
stream.seek(footerOffset); // may exceed current stream length
// after
if (footerOffset > streamSize) {
  throw new EOFException("Offset beyond stream: " + footerOffset + " > " + streamSize);
}
stream.seek(footerOffset);
Defensive patterns

Strategy: validation

Validate before calling

if (newPos > plainStreamSize) { throw new EOFException("Position " + newPos + " beyond stream length " + plainStreamSize); }

Type guard

boolean isWithinStream(long pos, long size) { return pos >= 0 && pos <= size; }

Try / catch

try { stream.seek(pos); } catch (EOFException e) { /* truncated/changed file: re-fetch and reopen */ reopenFile(); }

Prevention

When it happens

Trigger: Calling seek(newPos) where newPos > plainStreamSize — e.g. computed offsets from stale file metadata, a truncated encrypted file, or wrong plainStreamSize initialization.

Common situations: Reading an encrypted file that was truncated or overwritten after metadata (footer offsets) was computed; readers using offsets from a different file version.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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