juicedata/juicefs · error · EOFException

position is negative

Error message

position is negative

What it means

The private positioned read read(long pos, ByteBuffer) throws EOFException("position is negative") when the requested read offset is negative. This is a caller-contract guard before issuing jfs_pread to the native library; a negative pos is meaningless for a file offset. It surfaces as java.io.EOFException, consistent with Hadoop's positioned-read conventions.

Source

Thrown at sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java:1170

      if (got > 0) {
        srcBuf.limit(srcBuf.position() + got);
        b.put(srcBuf);
        buf.position(srcBuf.position());
        statistics.incrementBytesRead(got);
      }
      int more = read(position, b);
      if (more <= 0)
        return got > 0 ? got : -1;
      position += more;
      statistics.incrementBytesRead(more);
      buf.position(0);
      buf.limit(0);
      return got + more;
    }

    private synchronized int read(long pos, ByteBuffer b) throws IOException {
      if (pos < 0)
        throw new EOFException("position is negative");
      if (!b.hasRemaining())
        return 0;
      int got;
      int startPos = b.position();
      got = lib.jfs_pread(Thread.currentThread().getId(), fd, b, b.remaining(), pos);
      if (got == EINVAL)
        throw new IOException("stream was closed");
      if (got < 0)
        throw error(got, path);
      if (got == 0)
        return -1;
      b.position(startPos + got);
      return got;
    }

    @Override
    public synchronized void seek(long p) throws IOException {
      if (p < 0) {

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Clamp the offset: pos = Math.max(0, pos) before calling read(pos, ...).
  2. Fix the position arithmetic that produced the negative value (check seek/skip calculations).
  3. Validate any user- or metadata-supplied offset before passing it to positioned reads.

Example fix

// before
in.read(split.getStart() - headerLen, buf, 0, buf.length);
// after
long pos = Math.max(0, split.getStart() - headerLen);
in.read(pos, buf, 0, buf.length);
Defensive patterns

Strategy: validation

Validate before calling

long safePos = Math.max(0, pos);
if (safePos != pos) {
  LOG.warn("clamped negative read position {} -> {}", pos, safePos);
}
in.read(safePos, buf, off, len);

Try / catch

try {
  in.read(pos, buf, off, len);
} catch (EOFException e) {
  if ("position is negative".equals(e.getMessage())) {
    in.read(0, buf, off, len); // clamp and retry
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling in.read(negativePos, ...) directly, or via refill()/read(ByteBuffer) when the tracked position variable became negative through a bad seek/skip (e.g. seek to negative clamped by another path, or position decremented past zero).

Common situations: Custom readers computing pos = current - backoff with underflow; reading ahead of a seek(0)-then-read pattern where position arithmetic goes negative; wrappers that pass user-supplied offsets unvalidated.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/09c5999d4cbc6354. Report an issue: GitHub.