apache/hadoop · error · EOFException

Cannot seek to a negative offset

Error message

Cannot seek to a negative offset

What it means

Inside the bytebuffer block's ByteBufferInputStream.skip(offset), the new position is computed as position() + offset; if the result is negative the method throws EOFException(FSExceptionMessages.NEGATIVE_SEEK). Despite the 'seek' wording, this is triggered by skip(): skipping backwards by more than the current position inside an upload block buffer. It is a positional-arithmetic violation on an in-memory block, not a network or OBS service error.

Source

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

          throw new IOException(FSExceptionMessages.STREAM_IS_CLOSED);
        }
      }

      public synchronized int read() {
        if (available() > 0) {
          return byteBuffer.get() & OBSCommonUtils.BYTE_TO_INT_MASK;
        } else {
          return -1;
        }
      }

      @Override
      public synchronized long skip(final long offset)
          throws IOException {
        verifyOpen();
        long newPos = position() + offset;
        if (newPos < 0) {
          throw new EOFException(FSExceptionMessages.NEGATIVE_SEEK);
        }
        if (newPos > size) {
          throw new EOFException(
              FSExceptionMessages.CANNOT_SEEK_PAST_EOF);
        }
        byteBuffer.position((int) newPos);
        return newPos;
      }

      @Override
      public synchronized int available() {
        Preconditions.checkState(byteBuffer != null,
            FSExceptionMessages.STREAM_IS_CLOSED);
        return byteBuffer.remaining();
      }

      /**
       * Get the current buffer position.

View on GitHub (pinned to 2add963021)

Solutions

  1. Clamp the desired new position before calling skip: compute target = max(0, currentPosition + delta) and skip(target - currentPosition)
  2. Guard at the API boundary: reject or normalize negative skip requests before they reach the block stream
  3. If rewinding is genuinely needed, re-open the block stream from its start instead of using negative skip

Example fix

// before
long skipped = blockStream.skip(delta); // delta may push position < 0

// after
long pos = blockStream.position();
long clamped = Math.max(0, pos + delta);
long skipped = blockStream.skip(clamped - pos);
Defensive patterns

Strategy: validation

Validate before calling

// before calling skip(delta) on a block stream
long target = currentPosition() + delta;
if (target < 0) {
  target = 0; // or reject: throw new IllegalArgumentException("skip underflows position");
}
long toSkip = target - currentPosition();
if (toSkip != 0) blockStream.skip(toSkip);

Try / catch

try {
  blockStream.skip(delta);
} catch (EOFException e) {
  if (FSExceptionMessages.NEGATIVE_SEEK.equals(e.getMessage())) {
    // rewind past start requested: clamp to 0 and skip(-position) once
    blockStream.skip(-blockStream.position());
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling skip(n) where n is negative and |n| > current position within the block stream (e.g. at position 5, skip(-10)); passing a negative skip value directly; wrapper frameworks (record readers, compression codecs) that compute relative skips and occasionally go below zero.

Common situations: Custom InputStream wrappers that forward user-supplied offsets to skip(); record boundary resync logic that rewinds by a delta larger than bytes consumed; porting code from streams where negative skip is silently clamped to zero.

Related errors


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