{"record":{"id":"4ff55fb7c647f64c","repo":"apache/iceberg","slug":"cannot-seek-to-position-after-end-of-file-s-4ff55f","errorCode":null,"errorMessage":"Cannot seek to position after end of file: %s","messagePattern":"Cannot seek to position after end of file: (.+?)","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/io/SingleBufferInputStream.java","lineNumber":86,"sourceCode":"    if (len == 0) {\n      return 0;\n    }\n\n    int remaining = buffer.remaining();\n    if (remaining <= 0) {\n      return -1;\n    }\n\n    int bytesToRead = Math.min(buffer.remaining(), len);\n    buffer.get(bytes, off, bytesToRead);\n\n    return bytesToRead;\n  }\n\n  @Override\n  public void seek(long newPosition) throws IOException {\n    if (newPosition > length) {\n      throw new EOFException(\n          String.format(\"Cannot seek to position after end of file: %s\", newPosition));\n    }\n\n    if (getPos() > newPosition) {\n      // backwards seek requires returning to the initial state\n      initFromBuffer();\n    }\n\n    long bytesToSkip = newPosition - getPos();\n    skipFully(bytesToSkip);\n  }\n\n  @Override\n  public long skip(long len) {\n    if (len == 0) {\n      return 0;\n    }\n","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/io/SingleBufferInputStream.java#L68-L104","documentation":"SingleBufferInputStream.seek validates the target position against the underlying buffer length and throws EOFException when the position is past the end of the data. It is a seek() called with an offset beyond the readable bytes of the single backing buffer.","triggerScenarios":"Calling seek(newPosition) where newPosition > length of the stream, e.g. computing a record offset incorrectly, reading Avro footer/middle offsets against a truncated or shorter buffer, or seeking a relative offset from the wrong base.","commonSituations":"Corrupt or truncated data files; reading files split at byte boundaries with stale metadata offsets; an off-by-one using file length vs last valid position; deserializing an in-memory Avro buffer with offsets from a different file.","solutions":["Validate the target offset against the file/buffer length before seeking (offset must be <= length).","Re-read or re-download the data file — the buffer may be truncated or corrupt.","Check that offsets come from the same file version/snapshot the buffer was read from.","Fix off-by-one logic: last valid seek position equals length, not length + 1 of a record."],"exampleFix":"// before\nstream.seek(offset); // may throw EOFException\n\n// after\nif (offset <= stream.length()) {\n  stream.seek(offset);\n} else {\n  throw new IllegalStateException(\"Offset \" + offset + \" exceeds data length \" + stream.length());\n}","handlingStrategy":"validation","validationCode":"void seekSafe(SingleBufferInputStream in, long pos) throws IOException {\n  Preconditions.checkArgument(pos >= 0 && pos <= in.length(),\n      \"Seek target %s out of range [0, %s]\", pos, in.length());\n  in.seek(pos);\n}","typeGuard":null,"tryCatchPattern":"try {\n  stream.seek(offset);\n} catch (EOFException e) {\n  throw new CorruptDataException(\"Offset \" + offset + \" past end of data; file likely truncated\", e);\n}","preventionTips":["Bound every computed seek offset against the stream/buffer length first","Verify file checksums/sizes after download to catch truncation early","Ensure metadata offsets come from the same file version being read"],"tags":["seek","eof","input-stream"],"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-23T08:17:48.524Z"}