{"record":{"id":"e0a45d2dddca18cc","repo":"apache/iceberg","slug":"cannot-seek-to-position-after-end-of-file-s","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/MultiBufferInputStream.java","lineNumber":72,"sourceCode":"    initFromBuffers();\n  }\n\n  private void initFromBuffers() {\n    discardMark();\n    this.position = 0;\n    this.iterator = buffers.stream().map(ByteBuffer::duplicate).iterator();\n    nextBuffer();\n  }\n\n  @Override\n  public long getPos() {\n    return position;\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 (position > newPosition) {\n      // backward seek requires returning to the initial state\n      initFromBuffers();\n    }\n\n    long bytesToSkip = newPosition - position;\n    skipFully(bytesToSkip);\n  }\n\n  @Override\n  public long skip(long n) {\n    if (n <= 0) {\n      return 0;\n    }\n","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/io/MultiBufferInputStream.java#L54-L90","documentation":"MultiBufferInputStream backs a single stream view over a list of ByteBuffers and tracks the logical file length. seek(newPosition) beyond that length throws EOFException because the stream has no data there. It is a guard against reads past the logical end of the wrapped buffers.","triggerScenarios":"Calling seek(long) on a MultiBufferInputStream with a position greater than the total length of the supplied buffers — typically a metadata-driven seek (footer offset, row-group offset) that exceeds the data actually loaded, or passing a stale/incorrect offset.","commonSituations":"Parquet column readers seeking to a row-group offset from metadata that doesn't match the buffer slice loaded; loading only a partial file range (e.g. a split) but seeking with absolute file offsets; corrupted/stale footer offsets.","solutions":["Validate the target position against the stream length before seeking (position <= length)","Load the correct/full buffer range that contains the offset being seeked to","If slicing a file, seek with offsets relative to the slice, not the original file","Refresh metadata offsets if the file was rewritten","Catch EOFException and surface a clear 'offset beyond data' error to the caller"],"exampleFix":"// before\nstream.seek(footerOffset); // EOFException when footerOffset > loaded buffers\n// after\nif (footerOffset > stream.getLength()) {\n  throw new IOException(\"Offset beyond stream end: \" + footerOffset);\n}\nstream.seek(footerOffset);","handlingStrategy":"validation","validationCode":"if (newPosition > stream.getLength()) {\n  throw new IOException(\"Seek position \" + newPosition + \" beyond stream length \" + stream.getLength());\n}\nstream.seek(newPosition);","typeGuard":null,"tryCatchPattern":"try {\n  stream.seek(pos);\n} catch (EOFException e) {\n  throw new IOException(\"Seek beyond end of loaded data at \" + pos, e);\n}","preventionTips":["Compute seek offsets relative to the loaded buffer slice, not the original file","Keep metadata offsets in sync with the actual byte range loaded","Never share one MultiBufferInputStream across independent readers"],"tags":["io","seek","eof","byte-buffers"],"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"}