apache/hadoop · error · EOFException

Cannot seek to a negative offset {}

Error message

Cannot seek to a negative offset {}

What it means

OBSInputStream.seek first calls checkNotClosed() then rejects negative targets: seek(targetPos < 0) throws EOFException(FSExceptionMessages.NEGATIVE_SEEK + ' ' + targetPos). This matches the Hadoop FSDataInputStream contract that negative seeks are invalid. Note the EOFException type (not IOException subclass of choice one might expect) — catch blocks for EOF during reads will also catch this. When contentLength <= 0 the seek is a no-op after the check, so the error is purely about the caller passing a negative position.

Source

Thrown at hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSInputStream.java:272

        streamCurrentPos,
        nextReadPos,
        threadId,
        endTime - startTime
    );
  }

  @Override
  public synchronized long getPos() {
    return nextReadPos < 0 ? 0 : nextReadPos;
  }

  @Override
  public synchronized void seek(final long targetPos) throws IOException {
    checkNotClosed();

    // Do not allow negative seek
    if (targetPos < 0) {
      throw new EOFException(
          FSExceptionMessages.NEGATIVE_SEEK + " " + targetPos);
    }

    if (this.contentLength <= 0) {
      return;
    }

    // Lazy seek
    nextReadPos = targetPos;
  }

  /**
   * Seek without raising any exception. This is for use in {@code finally}
   * clauses
   *
   * @param positiveTargetPos a target position which must be positive.
   */
  private void seekQuietly(final long positiveTargetPos) {

View on GitHub (pinned to 2add963021)

Solutions

  1. Clamp seek targets: long target = Math.max(0, computedPosition) before calling seek
  2. Fix the position arithmetic — find where a length/count of -1 (common 'empty' sentinel) leaks into seek
  3. Unit-test edge splits: empty files, single-byte files, and split boundaries at 0 and contentLength
  4. Note EOFException is thrown, so don't blanket-catch EOFException around seek+read loops and mislabel it as premature EOF

Example fix

// before
long target = currentPos - rewindAmount; // can be negative
in.seek(target); // EOFException: Cannot seek to a negative offset -N

// after
long target = Math.max(0, currentPos - rewindAmount);
in.seek(target);
Defensive patterns

Strategy: validation

Validate before calling

long clampedTarget = Math.max(0, computedTarget);
if (clampedTarget != computedTarget) {
  LOG.debug("clamped negative seek target {} -> {}", computedTarget, clampedTarget);
}
in.seek(clampedTarget);

Try / catch

try {
  in.seek(target);
} catch (EOFException e) {
  if (String.valueOf(e.getMessage()).startsWith("Cannot seek to a negative offset")) {
    in.seek(0); // caller bug: negative position; clamp and continue, and fix the arithmetic
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: fs.open(...).seek(negativeValue), typically from arithmetic that computes target = current - delta without clamping at 0; record readers seeking to (index-1)-based offsets; parquet/orc readers computing row-group starts where an empty-file edge case yields -1.

Common situations: Custom InputFormats deriving split starts from offsets that underflow for the last/empty split; porting code from streams where seek(-x) rewound to 0 silently; off-by-one bugs in position bookkeeping after a skipped header.

Related errors


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