apache/iceberg · error · IOException

Invalid position: ${newPos}

Error message

Invalid position: ${newPos}

What it means

AesGcmInputStream.seek() rejects positions before the start of the plaintext stream. Calling seek with a negative newPos throws IOException("Invalid position: ..."). This is a plain argument validation protecting the underlying decrypting stream state.

Source

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

        remainingBytesToRead -= bytesToCopy;
        resultBufferOffset += bytesToCopy;
        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;

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Fix the caller's offset computation so it never produces negative positions
  2. Validate/clamp positions before calling seek
  3. Check whether an upstream read returned -1 (EOF sentinel) that was then used as a seek offset

Example fix

// before
stream.seek(offset); // offset may be -1 from a failed read
// after
if (offset >= 0) { stream.seek(offset); } else { throw new EOFException("No data read"); }
Defensive patterns

Strategy: validation

Validate before calling

if (newPos < 0) { throw new IllegalArgumentException("Position must be >= 0, got " + newPos); }

Type guard

boolean isValidSeek(long pos) { return pos >= 0; }

Try / catch

try { stream.seek(pos); } catch (IOException e) { throw new IllegalStateException("Seek failed: check offset computation", e); }

Prevention

When it happens

Trigger: Calling seek(negativeLong) on an AesGcmInputStream, typically from a reader computing offsets with signed/overflow arithmetic bugs or uninitialized offsets.

Common situations: Parquet/ORC readers restoring positions from uninitialized or corrupted page offsets; long underflow in offset arithmetic when reading encrypted files.

Related errors


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