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
- 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
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
- 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
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
- Invalid position: ${newPos}
- File length is null
- File length unknown, creating an AesGcmInputFile is not safe
- Failed to create file: %s
- Failed to delete: %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/5f47756a7b04db65.
Report an issue: GitHub.