{"record":{"id":"81f3e7064fd2ca91","repo":"apache/iceberg","slug":"invalid-position-newpos","errorCode":null,"errorMessage":"Invalid position: ${newPos}","messagePattern":"Invalid position: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/encryption/AesGcmInputStream.java","lineNumber":151,"sourceCode":"        remainingBytesToRead -= bytesToCopy;\n        resultBufferOffset += bytesToCopy;\n        this.plainStreamPosition += bytesToCopy;\n      } else if (available() > 0) {\n        decryptBlock(blockIndex(plainStreamPosition));\n\n      } else {\n        break;\n      }\n    }\n\n    // return -1 for EOF\n    return totalBytesRead > 0 ? totalBytesRead : -1;\n  }\n\n  @Override\n  public void seek(long newPos) throws IOException {\n    if (newPos < 0) {\n      throw new IOException(\"Invalid position: \" + newPos);\n    } else if (newPos > plainStreamSize) {\n      throw new EOFException(\n          \"Invalid position: \" + newPos + \" > stream length, \" + plainStreamSize);\n    }\n\n    this.plainStreamPosition = newPos;\n  }\n\n  @Override\n  public long skip(long n) {\n    if (n <= 0) {\n      return 0;\n    }\n\n    long bytesLeftInStream = plainStreamSize - plainStreamPosition;\n    if (n > bytesLeftInStream) {\n      // skip the rest of the stream\n      this.plainStreamPosition = plainStreamSize;","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/encryption/AesGcmInputStream.java#L133-L169","documentation":"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.","triggerScenarios":"Calling seek(negativeLong) on an AesGcmInputStream, typically from a reader computing offsets with signed/overflow arithmetic bugs or uninitialized offsets.","commonSituations":"Parquet/ORC readers restoring positions from uninitialized or corrupted page offsets; long underflow in offset arithmetic when reading encrypted files.","solutions":["Fix the caller's offset computation so it never produces negative positions","Validate/clamp positions before calling seek","Check whether an upstream read returned -1 (EOF sentinel) that was then used as a seek offset"],"exampleFix":"// before\nstream.seek(offset); // offset may be -1 from a failed read\n// after\nif (offset >= 0) { stream.seek(offset); } else { throw new EOFException(\"No data read\"); }","handlingStrategy":"validation","validationCode":"if (newPos < 0) { throw new IllegalArgumentException(\"Position must be >= 0, got \" + newPos); }","typeGuard":"boolean isValidSeek(long pos) { return pos >= 0; }","tryCatchPattern":"try { stream.seek(pos); } catch (IOException e) { throw new IllegalStateException(\"Seek failed: check offset computation\", e); }","preventionTips":["Never use read() return value -1 as a seek offset","Check offset arithmetic for long underflow/overflow","Validate positions against stream size before seeking"],"tags":["java","encryption","io","seek"],"backgroundTag":"argument-out-of-range","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}