{"record":{"id":"53b3c8cfa016dfa4","repo":"apache/iceberg","slug":"cannot-seek-to-position-newpos-exceeds-stream-l","errorCode":null,"errorMessage":"Cannot seek to position <newPos>: exceeds stream length <contents.length>","messagePattern":"Cannot seek to position <newPos>: exceeds stream length <contents\\.length>","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/io/EagerInputStream.java","lineNumber":48,"sourceCode":"  private final ByteArrayInputStream delegate;\n\n  EagerInputStream(byte[] contents) {\n    Preconditions.checkNotNull(contents, \"contents is null\");\n    this.contents = contents;\n    this.delegate = new ByteArrayInputStream(contents);\n  }\n\n  @Override\n  public long getPos() throws IOException {\n    return (long) contents.length - delegate.available();\n  }\n\n  @Override\n  public void seek(long newPos) throws IOException {\n    Preconditions.checkArgument(newPos >= 0, \"position is negative: %s\", newPos);\n    delegate.reset();\n    if (skip(newPos) != newPos) {\n      throw new EOFException(\n          \"Cannot seek to position \" + newPos + \": exceeds stream length \" + contents.length);\n    }\n  }\n\n  @Override\n  public int read() throws IOException {\n    return delegate.read();\n  }\n\n  @Override\n  public int read(byte[] b, int off, int len) throws IOException {\n    return delegate.read(b, off, len);\n  }\n\n  @Override\n  public long skip(long n) throws IOException {\n    return delegate.skip(n);\n  }","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/io/EagerInputStream.java#L30-L66","documentation":"EagerInputStream.seek validates the target position and throws EOFException when skipping to newPos goes past the end of the in-memory contents. Since the whole file is already in memory, its length is fixed and seeking beyond it is meaningless.","triggerScenarios":"Calling seek(newPos) on a stream obtained from EagerInputFile.newStream() where newPos >= contents.length (e.g. trust length of a Parquet footer offset from stale metadata).","commonSituations":"Corrupted or stale offset/length metadata in file footers; arithmetic bugs computing read positions; reading a truncated in-memory buffer with offsets meant for the full file.","solutions":["Check newPos against the stream length before seeking.","Recompute the offset — footer/row-group offsets may come from stale metadata.","Re-read the file to get fresh, correct contents.","Fix caller logic that assumes a larger file than the eager buffer holds."],"exampleFix":"// before\nstream.seek(offset); // may throw EOFException\n// after\nif (offset >= 0 && offset < fileSize) {\n  stream.seek(offset);\n} else {\n  throw new IllegalArgumentException(\"Offset \" + offset + \" outside file of length \" + fileSize);\n}","handlingStrategy":"validation","validationCode":"if (newPos < 0 || newPos >= file.getLength()) {\n  throw new IllegalArgumentException(\"seek position \" + newPos + \" out of bounds\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  stream.seek(pos);\n} catch (EOFException e) {\n  throw new IllegalArgumentException(\"Offset beyond eager buffer\", e);\n}","preventionTips":["Always bound-check offsets against file length before seeking","Recompute offsets if metadata might be stale","Use getLength() on the stream for bounds instead of assumed sizes","Verify file integrity when footer offsets exceed file size"],"tags":["seek","out-of-range","memory-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"}