{"record":{"id":"00674b2615467f6d","repo":"apache/hadoop","slug":"cannot-seek-after-eof-00674b","errorCode":null,"errorMessage":"Cannot seek after EOF","messagePattern":"Cannot seek after EOF","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java","lineNumber":1642,"sourceCode":"    if (n > 0) {\n      long curPos = getPos();\n      long fileLen = getFileLength();\n      if (n+curPos > fileLen) {\n        n = fileLen - curPos;\n      }\n      seek(curPos+n);\n      return n;\n    }\n    return n < 0 ? -1 : 0;\n  }\n\n  /**\n   * Seek to a new arbitrary location\n   */\n  @Override\n  public synchronized void seek(long targetPos) throws IOException {\n    if (targetPos > getFileLength()) {\n      throw new EOFException(\"Cannot seek after EOF\");\n    }\n    if (targetPos < 0) {\n      throw new EOFException(\"Cannot seek to negative offset\");\n    }\n    if (closed.get()) {\n      throw new IOException(\"Stream is closed!\");\n    }\n    boolean done = false;\n    if (pos <= targetPos && targetPos <= blockEnd) {\n      //\n      // If this seek is to a positive position in the current\n      // block, and this piece of data might already be lying in\n      // the TCP buffer, then just eat up the intervening data.\n      //\n      int diff = (int)(targetPos - pos);\n      if (diff <= blockReader.available()) {\n        try {\n          pos += blockReader.skip(diff);","sourceCodeStart":1624,"sourceCodeEnd":1660,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java#L1624-L1660","documentation":"seek() validates the target against the stream's current file length: targetPos > getFileLength() throws EOFException (a sibling guard rejects negative targets, and a closed stream throws 'Stream is closed!'). It fires when the caller seeks to a position computed from a stale, larger length - typically after the file was truncated or when tail logic extrapolates past EOF.","triggerScenarios":"seek(target) where target exceeds the current length: offsets cached from an earlier, longer version of the file; racing a truncate; index/offset files pointing past the current data extent.","commonSituations":"Tailing loops that remember an old file length across rotation/truncation; readers using saved checkpoints after external truncation; off-by-one seek to len instead of len-1.","solutions":["Clamp the target to the freshly fetched file length before seeking","Re-stat the file (and re-open if it was truncated/replaced) before seeking to saved offsets","In tail loops, poll length growth and only seek within [0, currentLength]"],"exampleFix":"// before\nin.seek(savedOffset); // saved when the file was longer\n\n// after\nlong len = fs.getFileStatus(path).getLen();\nin.seek(Math.min(savedOffset, len));","handlingStrategy":"validation","validationCode":"long len = fs.getFileStatus(path).getLen();\nif (targetPos > len) {\n  targetPos = len; // clamp: seek to EOF instead of past it\n}\nif (targetPos < 0) {\n  throw new IllegalArgumentException(\"negative seek: \" + targetPos);\n}\nin.seek(targetPos);","typeGuard":"static boolean isSeekPastEof(IOException e) {\n  return e instanceof EOFException\n      && e.getMessage() != null && e.getMessage().equals(\"Cannot seek after EOF\");\n}","tryCatchPattern":"try {\n  in.seek(targetPos);\n} catch (EOFException e) {\n  if (isSeekPastEof(e)) {\n    long len = fs.getFileStatus(path).getLen();\n    in.seek(Math.min(targetPos, len)); // file shrank: clamp\n  } else throw e;\n}","preventionTips":["Clamp seek targets to the current file length from a fresh stat","In tail loops, only seek to positions confirmed by the latest length","Re-open streams after truncate/rotation events instead of reusing stale offsets"],"tags":["hdfs","seek","eof","validation"],"backgroundTag":"seek-past-eof","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}