apache/hadoop · error · EOFException

Cannot seek to negative offset

Error message

Cannot seek to negative offset

What it means

DFSInputStream.seek(long) validates the requested position before doing anything: any targetPos < 0 throws java.io.EOFException('Cannot seek to negative offset'). HDFS files start at offset 0, so a negative target is always a caller bug (bad offset arithmetic), not a transient cluster condition. Note the odd choice of EOFException, which pairs it with the 'Cannot seek after EOF' check just above it.

Source

Thrown at hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java:1645

      if (n+curPos > fileLen) {
        n = fileLen - curPos;
      }
      seek(curPos+n);
      return n;
    }
    return n < 0 ? -1 : 0;
  }

  /**
   * Seek to a new arbitrary location
   */
  @Override
  public synchronized void seek(long targetPos) throws IOException {
    if (targetPos > getFileLength()) {
      throw new EOFException("Cannot seek after EOF");
    }
    if (targetPos < 0) {
      throw new EOFException("Cannot seek to negative offset");
    }
    if (closed.get()) {
      throw new IOException("Stream is closed!");
    }
    boolean done = false;
    if (pos <= targetPos && targetPos <= blockEnd) {
      //
      // If this seek is to a positive position in the current
      // block, and this piece of data might already be lying in
      // the TCP buffer, then just eat up the intervening data.
      //
      int diff = (int)(targetPos - pos);
      if (diff <= blockReader.available()) {
        try {
          pos += blockReader.skip(diff);
          if (pos == targetPos) {
            done = true;
          } else {

View on GitHub (pinned to 2add963021)

Solutions

  1. Fix the offset arithmetic so it can never go below 0: clamp with Math.max(0, computedPos) before calling seek().
  2. Audit every expression feeding seek() for subtraction that can underflow (pos - headerLen, splitStart - 1, pos - recordLen) and guard each one.
  3. If the negative value comes from parsed metadata, validate it and fail fast with a clear message about the corrupt index instead of letting seek() throw.
  4. Add a boundary unit test at position 0 and position 1 to lock in the clamping behavior.

Example fix

// before
long prevStart = in.getPos() - recordHeaderLen;
in.seek(prevStart); // throws when getPos() < recordHeaderLen

// after
long prevStart = Math.max(0, in.getPos() - recordHeaderLen);
in.seek(prevStart);
Defensive patterns

Strategy: validation

Validate before calling

long targetPos = computedOffset; // whatever the caller computed
long fileLen = fs.getFileStatus(path).getLen();
if (targetPos < 0 || targetPos > fileLen) {
  throw new IllegalArgumentException(
      "seek target " + targetPos + " outside [0, " + fileLen + "] for " + path);
}
in.seek(targetPos);

Try / catch

try {
  in.seek(targetPos);
} catch (EOFException e) {
  // negative or past-EOF target: programming error in offset math - fail loudly, do not retry
  throw new IllegalArgumentException("bad seek offset " + targetPos, e);
}

Prevention

When it happens

Trigger: Calling fsDataInputStream.seek(n) with n negative, e.g. seek(getPos() - delta) where delta > getPos(), or seek(-1). It is NOT produced by skip(): skip() clamps positive n to the file length before delegating to seek(), so only direct seek() calls with an under-flowing computed offset hit it.

Common situations: Record readers that compute a previous-record start as (currentPos - recordLength) and underflow at the first record; offsets parsed from corrupted index/footer metadata; index rebuild tools that subtract a header size larger than the current position; ported code that assumed skip()/seek() silently accept negative values.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/90831947e1c35a99. Report an issue: GitHub.