{"record":{"id":"d1b0dc8303827163","repo":"apache/hadoop","slug":"cannot-seek-to-a-negative-offset-d1b0dc","errorCode":null,"errorMessage":"Cannot seek to a negative offset","messagePattern":"Cannot seek to a negative offset","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSDataBlocks.java","lineNumber":713,"sourceCode":"          throw new IOException(FSExceptionMessages.STREAM_IS_CLOSED);\n        }\n      }\n\n      public synchronized int read() {\n        if (available() > 0) {\n          return byteBuffer.get() & OBSCommonUtils.BYTE_TO_INT_MASK;\n        } else {\n          return -1;\n        }\n      }\n\n      @Override\n      public synchronized long skip(final long offset)\n          throws IOException {\n        verifyOpen();\n        long newPos = position() + offset;\n        if (newPos < 0) {\n          throw new EOFException(FSExceptionMessages.NEGATIVE_SEEK);\n        }\n        if (newPos > size) {\n          throw new EOFException(\n              FSExceptionMessages.CANNOT_SEEK_PAST_EOF);\n        }\n        byteBuffer.position((int) newPos);\n        return newPos;\n      }\n\n      @Override\n      public synchronized int available() {\n        Preconditions.checkState(byteBuffer != null,\n            FSExceptionMessages.STREAM_IS_CLOSED);\n        return byteBuffer.remaining();\n      }\n\n      /**\n       * Get the current buffer position.","sourceCodeStart":695,"sourceCodeEnd":731,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-huaweicloud/src/main/java/org/apache/hadoop/fs/obs/OBSDataBlocks.java#L695-L731","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Clamp the desired new position before calling skip: compute target = max(0, currentPosition + delta) and skip(target - currentPosition)","Guard at the API boundary: reject or normalize negative skip requests before they reach the block stream","If rewinding is genuinely needed, re-open the block stream from its start instead of using negative skip"],"exampleFix":"// before\nlong skipped = blockStream.skip(delta); // delta may push position < 0\n\n// after\nlong pos = blockStream.position();\nlong clamped = Math.max(0, pos + delta);\nlong skipped = blockStream.skip(clamped - pos);","handlingStrategy":"validation","validationCode":"// before calling skip(delta) on a block stream\nlong target = currentPosition() + delta;\nif (target < 0) {\n  target = 0; // or reject: throw new IllegalArgumentException(\"skip underflows position\");\n}\nlong toSkip = target - currentPosition();\nif (toSkip != 0) blockStream.skip(toSkip);","typeGuard":null,"tryCatchPattern":"try {\n  blockStream.skip(delta);\n} catch (EOFException e) {\n  if (FSExceptionMessages.NEGATIVE_SEEK.equals(e.getMessage())) {\n    // rewind past start requested: clamp to 0 and skip(-position) once\n    blockStream.skip(-blockStream.position());\n  } else {\n    throw e;\n  }\n}","preventionTips":["Never pass unclamped relative deltas to skip()","Track positions in your reader rather than trusting skip to bound-check","Unit-test skip at position 0 and at block boundaries"],"tags":["positioning","invalid-argument","io","hadoop-obs"],"backgroundTag":"negative-seek-offset","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}