{"record":{"id":"f8fc884ec9adbdb4","repo":"apache/hadoop","slug":"cannot-seek-to-negative-offset-f8fc88","errorCode":null,"errorMessage":"Cannot seek to negative offset","messagePattern":"Cannot seek to negative offset","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSStripedInputStream.java","lineNumber":360,"sourceCode":"    }\n    updateReadStatistics(readStatistics, stats.getBytesRead(),\n        stats.isShortCircuit(), stats.getNetworkDistance());\n    dfsClient.updateFileSystemReadStats(stats.getNetworkDistance(),\n        stats.getBytesRead(), readTimeMS);\n    assert readStatistics.getBlockType() == BlockType.STRIPED;\n    dfsClient.updateFileSystemECReadStats(stats.getBytesRead());\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    if (targetPos <= blockEnd) {\n      final long targetOffsetInBlk = getOffsetInBlockGroup(targetPos);\n      if (curStripeRange.include(targetOffsetInBlk)) {\n        int bufOffset = getStripedBufOffset(targetOffsetInBlk);\n        curStripeBuf.position(bufOffset);\n        pos = targetPos;\n        return;\n      }\n    }\n    pos = targetPos;\n    blockEnd = -1;\n  }\n\n  private int getStripedBufOffset(long offsetInBlockGroup) {","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSStripedInputStream.java#L342-L378","documentation":"DFSStripedInputStream.seek(long) validates the target position before moving the read cursor of an erasure-coded HDFS file: positions past EOF and negative positions are both rejected. A negative targetPos throws EOFException('Cannot seek to negative offset') even though the stream itself is healthy — the value passed in is illegal. The striped reader shares this strictness with the replicated reader (DFSInputStream), because internal stripe buffers are indexed from the file start.","triggerScenarios":"Calling seek(n) with n < 0 on an FSDataInputStream opened from an EC-coded HDFS file. Typical producers: backtracking math like pos - backTrack where backTrack > pos, and passing a -1 sentinel (e.g., the result of String.indexOf()/lastIndexOf()) directly into seek().","commonSituations":"Custom skip/rewind logic that subtracts from the current position without clamping; index-derived offsets not checked for -1; code ported from APIs where seek wrapped around or was a no-op on bad input.","solutions":["Clamp the computed position before seeking: Math.max(0, targetPos).","Audit all seek call sites for underflow-prone arithmetic and unchecked indexOf()/lastIndexOf() results.","Map sentinel values (e.g., -1 meaning 'not found') to a real decision — skip the seek or seek(0) — instead of forwarding them.","As a safety net, catch EOFException around seek() and log-and-correct rather than failing the whole read job."],"exampleFix":"// before\nlong newPos = currentPos - backTrack;\nin.seek(newPos); // EOFException when backTrack > currentPos\n\n// after\nlong newPos = Math.max(0, currentPos - backTrack);\nin.seek(newPos);","handlingStrategy":"validation","validationCode":"if (targetPos < 0) {\n  throw new IllegalArgumentException(\n      \"seek position must be >= 0, got \" + targetPos);\n}\nif (targetPos > in.getLen()) { // also avoids 'Cannot seek after EOF'\n  targetPos = in.getLen();\n}\nin.seek(targetPos);","typeGuard":null,"tryCatchPattern":"try {\n  in.seek(targetPos);\n} catch (EOFException e) {\n  LOG.warn(\"Invalid seek target {} on {} - resetting to 0\", targetPos, path, e);\n  in.seek(0);\n}","preventionTips":["Never feed indexOf()/lastIndexOf() results into seek() without checking for -1.","Clamp every computed position: Math.max(0, Math.min(pos, fileLen)).","Validate both bounds — seek() also throws 'Cannot seek after EOF' for targets past the file length."],"tags":["hdfs","erasure-coding","input-stream","seek","argument-validation"],"backgroundTag":"stream-seek-out-of-bounds","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}