{"record":{"id":"4ec04d1db58c6e09","repo":"apache/druid","slug":"pos-d-out-of-range-d-d-4ec04d","errorCode":null,"errorMessage":"pos %d out of range [%d, %d]","messagePattern":"pos (.+?) out of range \\[(.+?), (.+?)\\]","errorType":"exception","errorClass":"org.apache.druid.java.util.common.IAE","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/writeout/FileWriteOutBytes.java","lineNumber":156,"sourceCode":"\n  @Override\n  public void writeTo(WritableByteChannel channel) throws IOException\n  {\n    flush();\n    ch.position(0);\n    try {\n      ByteStreams.copy(ch, channel);\n    }\n    finally {\n      ch.position(ch.size());\n    }\n  }\n\n  @Override\n  public void readFully(long pos, ByteBuffer buffer) throws IOException\n  {\n    if (pos < 0 || pos > writeOutBytes) {\n      throw new IAE(\"pos %d out of range [%d, %d]\", pos, 0, writeOutBytes);\n    }\n    flush();\n    ch.read(buffer, pos);\n    if (buffer.remaining() > 0) {\n      throw new BufferUnderflowException();\n    }\n  }\n\n  @Override\n  public InputStream asInputStream() throws IOException\n  {\n    flush();\n    return new FileInputStream(file);\n  }\n\n  @Override\n  public boolean isOpen()\n  {","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/writeout/FileWriteOutBytes.java#L138-L174","documentation":"FileWriteOutBytes.readFully validates pos against [0, writeOutBytes] (the current logical file size) before reading from the channel. An IAE is thrown for positions outside this range, guarding against reads past written data or before the start of the file.","triggerScenarios":"readFully(pos, buffer) with pos < 0 or pos > current written size: stale offsets after file shrink/rewrite, offsets from a different segment version, or overflowed computed offsets.","commonSituations":"Reading a segment index built with an older writer version; offset table out of sync after partial flush; copy/paste using file length on disk vs logical size mismatch; concurrent modification of the file.","solutions":["Validate/recompute the offset from the current writer (writeOutBytes) before calling readFully.","Flush pending writes (flushIfNeeded/writeTo) so logical size matches on-disk size, then re-read.","If the offset source is a persisted index, verify segment integrity (checksums) and rebuild the segment.","Check for version skew between the code writing offsets and the code reading them."],"exampleFix":"// before\nfileWriteOutBytes.readFully(storedOffset, buf); // storedOffset from old index\n// after\nlong pos = Math.min(storedOffset, fileWriteOutBytes.writeOutBytes);\nif (pos != storedOffset) {\n  log.warn(\"clamping stale offset %d -> %d\", storedOffset, pos);\n}\nfileWriteOutBytes.readFully(pos, buf);","handlingStrategy":"validation","validationCode":"if (pos < 0 || pos > fileWriteOutBytes.writeOutBytes) throw new IllegalArgumentException(\"invalid pos: \" + pos);","typeGuard":null,"tryCatchPattern":"try { w.readFully(pos, buf); } catch (IllegalArgumentException e) { throw new IOException(\"offset out of range: \" + pos, e); }","preventionTips":["Flush before reading back data from the same writer.","Regenerate offset metadata when writer version changes.","Validate checksums of segment/index files before trusting offsets.","Clamp or reject stale offsets instead of passing them through."],"tags":["segment-writeout","range-check","offset"],"backgroundTag":"value-out-of-range","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}