apache/hadoop · error · EOFException

Cannot seek to a negative offset

Error message

Cannot seek to a negative offset

What it means

CryptoInputStream.seek(pos) validates pos >= 0 first and throws EOFException with FSExceptionMessages.NEGATIVE_SEEK ("Cannot seek to a negative offset") for negative positions. This is a caller-input error independent of wrapped-stream capabilities; Hadoop filesystem code deliberately uses EOFException for negative seeks, which can mislead readers expecting IllegalArgumentException.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/CryptoInputStream.java:526

          + " does not support positioned readFully.");
    }
    ((PositionedReadable) in).readFully(position, buffer, offset, length);
    if (length > 0) {
      // This operation does not change the current offset of the file
      decrypt(position, buffer, offset, length);
    }
  }

  @Override
  public void readFully(long position, byte[] buffer) throws IOException {
    readFully(position, buffer, 0, buffer.length);
  }

  /** Seek to a position. */
  @Override
  public void seek(long pos) throws IOException {
    if (pos < 0) {
      throw new EOFException(FSExceptionMessages.NEGATIVE_SEEK);
    }
    checkStream();
    /*
     * If data of target pos in the underlying stream has already been read
     * and decrypted in outBuffer, we just need to re-position outBuffer.
     */
    if (pos <= streamOffset && pos >= (streamOffset - outBuffer.remaining())) {
      int forward = (int) (pos - (streamOffset - outBuffer.remaining()));
      if (forward > 0) {
        outBuffer.position(outBuffer.position() + forward);
      }
    } else {
      if (!(in instanceof Seekable)) {
        throw new UnsupportedOperationException(in.getClass().getCanonicalName()
            + " does not support seek.");
      }
      ((Seekable) in).seek(pos);
      resetStreamOffset(pos);

View on GitHub (pinned to 2add963021)

Solutions

  1. Clamp the computed offset to >= 0 before seek: Math.max(0L, target)
  2. Fix the upstream arithmetic that produced the negative position (overshoot in skip/read accounting)
  3. If the intent was 'go back n bytes', verify getPos() >= n before computing the target

Example fix

// before
long target = stream.getPos() - bytesConsumed; // can go negative
stream.seek(target);

// after
long target = Math.max(0L, stream.getPos() - bytesConsumed);
stream.seek(target);
Defensive patterns

Strategy: validation

Validate before calling

if (targetPos < 0) {
  throw new IllegalArgumentException("seek offset must be >= 0, got " + targetPos);
}
in.seek(targetPos);

Try / catch

try {
  in.seek(pos);
} catch (EOFException e) {
  if (FSExceptionMessages.NEGATIVE_SEEK.equals(e.getMessage())) {
    throw new IllegalArgumentException("computed a negative offset: " + pos, e);
  }
  throw e;
}

Prevention

When it happens

Trigger: seek(-1) or any negative position, usually from upstream arithmetic: getPos() minus an overshoot, seek(pos - n) with n > pos, or negative split/index offsets read from a corrupt index file.

Common situations: Reader code computing 'current position - bytesToSkip' without clamping at 0; off-by-one loops in custom InputFormats; offsets from malformed sidecar index files.

Related errors


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