{"record":{"id":"5f47756a7b04db65","repo":"apache/iceberg","slug":"invalid-position-newpos-stream-length-pla","errorCode":null,"errorMessage":"Invalid position: ${newPos} > stream length, ${plainStreamSize}","messagePattern":"Invalid position: (.+?) > stream length, (.+?)","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/encryption/AesGcmInputStream.java","lineNumber":153,"sourceCode":"        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;\n      return bytesLeftInStream;\n    }","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/encryption/AesGcmInputStream.java#L135-L171","documentation":"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.","triggerScenarios":"Calling seek(newPos) where newPos > plainStreamSize — e.g. computed offsets from stale file metadata, a truncated encrypted file, or wrong plainStreamSize initialization.","commonSituations":"Reading an encrypted file that was truncated or overwritten after metadata (footer offsets) was computed; readers using offsets from a different file version.","solutions":["Re-read file length/metadata so seek targets match the actual stream size","Check whether the underlying encrypted file was truncated or replaced; re-fetch the correct file","Validate newPos against plainStreamSize before seeking and handle gracefully"],"exampleFix":"// before\nstream.seek(footerOffset); // may exceed current stream length\n// after\nif (footerOffset > streamSize) {\n  throw new EOFException(\"Offset beyond stream: \" + footerOffset + \" > \" + streamSize);\n}\nstream.seek(footerOffset);","handlingStrategy":"validation","validationCode":"if (newPos > plainStreamSize) { throw new EOFException(\"Position \" + newPos + \" beyond stream length \" + plainStreamSize); }","typeGuard":"boolean isWithinStream(long pos, long size) { return pos >= 0 && pos <= size; }","tryCatchPattern":"try { stream.seek(pos); } catch (EOFException e) { /* truncated/changed file: re-fetch and reopen */ reopenFile(); }","preventionTips":["Re-read file size metadata before seeking to offsets from footers","Verify the encrypted file was not truncated/replaced after metadata was computed","Clamp or check offsets against plainStreamSize before every seek"],"tags":["java","encryption","io","seek"],"backgroundTag":"value-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"}