{"record":{"id":"bee73ff8924fcc7d","repo":"apache/hadoop","slug":"cannot-seek-to-a-negative-offset-pos-bee73f","errorCode":null,"errorMessage":"Cannot seek to a negative offset \" + pos","messagePattern":"Cannot seek to a negative offset \" \\+ pos","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/prefetch/S3ARemoteInputStream.java","lineNumber":463,"sourceCode":"      return \"closed\";\n    }\n\n    StringBuilder sb = new StringBuilder();\n    sb.append(String.format(\"nextReadPos = (%d)%n\", nextReadPos));\n    sb.append(String.format(\"fpos = (%s)\", fpos));\n    return sb.toString();\n  }\n\n  protected void throwIfClosed() throws IOException {\n    if (closed) {\n      throw new IOException(\n          name + \": \" + FSExceptionMessages.STREAM_IS_CLOSED);\n    }\n  }\n\n  protected void throwIfInvalidSeek(long pos) throws EOFException {\n    if (pos < 0) {\n      throw new EOFException(FSExceptionMessages.NEGATIVE_SEEK + \" \" + pos);\n    } else if (pos > this.getBlockData().getFileSize()) {\n      throw new EOFException(FSExceptionMessages.CANNOT_SEEK_PAST_EOF + \" \" + pos);\n    }\n  }\n\n  // Unsupported functions.\n\n  @Override\n  public void mark(int readlimit) {\n    throw new UnsupportedOperationException(\"mark not supported\");\n  }\n\n  @Override\n  public void reset() {\n    throw new UnsupportedOperationException(\"reset not supported\");\n  }\n\n  @Override","sourceCodeStart":445,"sourceCodeEnd":481,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/prefetch/S3ARemoteInputStream.java#L445-L481","documentation":"S3ARemoteInputStream.throwIfInvalidSeek() throws EOFException(FSExceptionMessages.NEGATIVE_SEEK + \" \" + pos) when seek() is called with a position < 0. The stream serves block data of a prefetched S3 object, and negative offsets are meaningless; this is a caller arithmetic bug.","triggerScenarios":"seek() with a negative computed offset on a stream using the prefetcher (fs.s3a.input.stream.type=Prefetch); offset underflow in custom split/RecordReader math; passing -1 'not set' sentinels from higher layers into seek.","commonSituations":"RecordReaders computing (splitStart - headerBytes); code ported from local-file assumptions; retry logic seeking to previous positions after consuming the whole stream (getPos semantics confusion).","solutions":["Clamp seek targets to >= 0 before calling seek().","Audit offset arithmetic (subtractions, casting int->long) that feeds seek positions.","Prefer FSDataInputStream.seekToNewSource-style guarded APIs or check getPos() first.","Write a unit test asserting all computed seek offsets are within [0, fileLen)."],"exampleFix":"// before\nlong pos = split.getStart() - prefixLen; // may be negative\nin.seek(pos);\n\n// after\nlong pos = Math.max(0, split.getStart() - prefixLen);\nin.seek(pos);","handlingStrategy":"validation","validationCode":"if (pos < 0) {\n  throw new IllegalArgumentException(\"seek position must be >= 0, got \" + pos);\n}\nin.seek(pos);","typeGuard":null,"tryCatchPattern":"try {\n  in.seek(pos);\n} catch (EOFException e) {\n  LOG.error(\"invalid seek to {}\", pos, e); // caller arithmetic bug\n  throw e;\n}","preventionTips":["Clamp computed offsets with Math.max(0, value).","Guard split arithmetic in RecordReaders with assertions on [0, fileLen).","Never pass -1 sentinels into seek()."],"tags":["hadoop-aws","prefetch","seek","off-by-error"],"backgroundTag":"negative-seek-offset","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}