{"record":{"id":"09c5999d4cbc6354","repo":"juicedata/juicefs","slug":"position-is-negative","errorCode":null,"errorMessage":"position is negative","messagePattern":"position is negative","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java","lineNumber":1170,"sourceCode":"      if (got > 0) {\n        srcBuf.limit(srcBuf.position() + got);\n        b.put(srcBuf);\n        buf.position(srcBuf.position());\n        statistics.incrementBytesRead(got);\n      }\n      int more = read(position, b);\n      if (more <= 0)\n        return got > 0 ? got : -1;\n      position += more;\n      statistics.incrementBytesRead(more);\n      buf.position(0);\n      buf.limit(0);\n      return got + more;\n    }\n\n    private synchronized int read(long pos, ByteBuffer b) throws IOException {\n      if (pos < 0)\n        throw new EOFException(\"position is negative\");\n      if (!b.hasRemaining())\n        return 0;\n      int got;\n      int startPos = b.position();\n      got = lib.jfs_pread(Thread.currentThread().getId(), fd, b, b.remaining(), pos);\n      if (got == EINVAL)\n        throw new IOException(\"stream was closed\");\n      if (got < 0)\n        throw error(got, path);\n      if (got == 0)\n        return -1;\n      b.position(startPos + got);\n      return got;\n    }\n\n    @Override\n    public synchronized void seek(long p) throws IOException {\n      if (p < 0) {","sourceCodeStart":1152,"sourceCodeEnd":1188,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java#L1152-L1188","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Clamp the offset: pos = Math.max(0, pos) before calling read(pos, ...).","Fix the position arithmetic that produced the negative value (check seek/skip calculations).","Validate any user- or metadata-supplied offset before passing it to positioned reads."],"exampleFix":"// before\nin.read(split.getStart() - headerLen, buf, 0, buf.length);\n// after\nlong pos = Math.max(0, split.getStart() - headerLen);\nin.read(pos, buf, 0, buf.length);","handlingStrategy":"validation","validationCode":"long safePos = Math.max(0, pos);\nif (safePos != pos) {\n  LOG.warn(\"clamped negative read position {} -> {}\", pos, safePos);\n}\nin.read(safePos, buf, off, len);","typeGuard":null,"tryCatchPattern":"try {\n  in.read(pos, buf, off, len);\n} catch (EOFException e) {\n  if (\"position is negative\".equals(e.getMessage())) {\n    in.read(0, buf, off, len); // clamp and retry\n  } else {\n    throw e;\n  }\n}","preventionTips":["Clamp all computed offsets with Math.max(0, ...) before positioned reads.","Review seek/skip arithmetic for underflow when subtracting lookahead amounts.","Validate user- or metadata-supplied offsets at input boundaries."],"tags":["java","hadoop","pread","eof-exception","argument-validation"],"backgroundTag":"value-out-of-range","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}