{"record":{"id":"e889962c2332282e","repo":"apache/hadoop","slug":"invalid-seek-offset-position-value-d-must-be-b","errorCode":null,"errorMessage":"Invalid seek offset: position value (%d) must be between 0 and %d for '%s'","messagePattern":"Invalid seek offset: position value \\((.+?)\\) must be between 0 and (.+?) for '(.+?)'","errorType":"exception","errorClass":"EOFException","httpStatus":null,"severity":"error","filePath":"hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageClientReadChannel.java","lineNumber":596,"sourceCode":"          resourceId.getBucketName(), resourceId.getObjectName(), new IOException(msg, error));\n    case OUT_OF_RANGE:\n      return (IOException) new EOFException(msg).initCause(error);\n    default:\n      return new IOException(msg, error);\n    }\n  }\n\n  /** Validates that the given position is valid for this channel. */\n  private void validatePosition(long position) throws IOException {\n    if (position < 0) {\n      throw new EOFException(\n          String.format(\n              \"Invalid seek offset: position value (%d) must be >= 0 for '%s'\",\n              position, resourceId));\n    }\n\n    if (objectSize >= 0 && position >= objectSize) {\n      throw new EOFException(\n          String.format(\n              \"Invalid seek offset: position value (%d) must be between 0 and %d for '%s'\",\n              position, objectSize, resourceId));\n    }\n  }\n\n  /** Throws if this channel is not currently open. */\n  private void throwIfNotOpen() throws IOException {\n    if (!isOpen()) {\n      throw new ClosedChannelException();\n    }\n  }\n}\n","sourceCodeStart":578,"sourceCodeEnd":610,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-cloud-storage-project/hadoop-gcp/src/main/java/org/apache/hadoop/fs/gs/GoogleCloudStorageClientReadChannel.java#L578-L610","documentation":"The second branch of validatePosition throws EOFException when objectSize >= 0 and the requested position is >= objectSize: seeks must land strictly inside [0, objectSize). Seeks to exactly size are rejected even though read() at that point would return -1. gzip-encoded objects never hit this branch because their objectSize is Long.MAX_VALUE.","triggerScenarios":"Calling position(n) / seek with n >= objectSize: readers seeking to end-of-file to force EOF, split computations built from stale FileStatus sizes, or stale channel size after a concurrent overwrite shrank the object.","commonSituations":"InputSplits computed from outdated listings; frameworks that 'seek to size' as a termination idiom; files replaced by shorter versions while a reader held old metadata.","solutions":["Detect EOF by reading (read() == -1) instead of seeking to size; remove seek-to-size idioms.","Refresh file status (getItemInfo/getFileStatus) before computing offsets from size.","Clamp: position = Math.min(position, size - 1) only when a valid read position is actually required."],"exampleFix":"// before\nif (hasMoreData) { ch.position(size); } // throws when position >= size\n\n// after\nif (hasMoreData) {\n  int b = ch.read(ByteBuffer.allocate(1));\n  boolean eof = (b == -1);\n}","handlingStrategy":"validation","validationCode":"long size = ch.size();\nif (position >= size) {\n  return; // at/after EOF: nothing to seek to\n}\nch.position(position);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use read() == -1 as the EOF signal; never seek to size as a termination idiom.","Recompute splits from a fresh FileStatus immediately before reading.","Assume sizes can change under you: re-stat on EOFException mentioning seek offsets."],"tags":["gcs","seek","eof","validation","read-channel"],"backgroundTag":"seek-past-end-of-file","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}