{"record":{"id":"2a9d4eb40b32aa17","repo":"apache/druid","slug":"endframe-d-numframes-d","errorCode":null,"errorMessage":"endFrame[%,d] > numFrames[%,d]","messagePattern":"endFrame\\[%,d\\] > numFrames\\[%,d\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/frame/channel/ReadableFileFrameChannel.java","lineNumber":67,"sourceCode":"      final int endFrame,\n      final WireTransferable.ConcreteDeserializer deserializer\n  )\n  {\n    this.frameFile = frameFile;\n    this.deserializer = deserializer;\n    this.currentFrame = startFrame;\n    this.endFrame = endFrame;\n\n    if (startFrame < 0) {\n      throw new IAE(\"startFrame[%,d] < 0\", startFrame);\n    }\n\n    if (startFrame > endFrame) {\n      throw new IAE(\"startFrame[%,d] > endFrame[%,d]\", startFrame, endFrame);\n    }\n\n    if (endFrame > frameFile.numFrames()) {\n      throw new IAE(\"endFrame[%,d] > numFrames[%,d]\", endFrame, frameFile.numFrames());\n    }\n  }\n\n  public ReadableFileFrameChannel(\n      final FrameFile frameFile,\n      final WireTransferable.ConcreteDeserializer deserializer\n  )\n  {\n    this(frameFile, 0, frameFile.numFrames(), deserializer);\n  }\n\n  @Override\n  public boolean isFinished()\n  {\n    return currentFrame == endFrame;\n  }\n\n  @Override","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/frame/channel/ReadableFileFrameChannel.java#L49-L85","documentation":"ReadableFileFrameChannel wraps a memory-mapped FrameFile and exposes frames [startFrame, endFrame) for reading. The constructor validates the requested frame range against the file and throws IAE when endFrame exceeds the number of frames actually present in the FrameFile. This guards against reading past the end of an immutable, on-disk frame file.","triggerScenarios":"Constructing ReadableFileFrameChannel(frameFile, startFrame, endFrame, deserializer) with endFrame > frameFile.numFrames(); e.g. splitting a frame file for parallel reads and computing sub-ranges from stale frame counts, or the frame file was rewritten/truncated with fewer frames after the range was computed.","commonSituations":"Off-by-one or exclusive-vs-inclusive range confusion when partitioning frames across workers; reading a frame file that was deleted or rewritten between counting frames and opening the channel; deserializing a persisted partition descriptor from an older run against a newly written file.","solutions":["Check frameFile.numFrames() before constructing and clamp endFrame to it.","Verify the frame range was computed against the same FrameFile instance/version that is being opened.","Use the two-argument constructor ReadableFileFrameChannel(frameFile, deserializer) to read all frames and avoid manual range math.","Fix off-by-one logic: the range is [startFrame, endFrame), so endFrame may equal numFrames but not exceed it."],"exampleFix":"// before\nnew ReadableFileFrameChannel(frameFile, start, end, deserializer);\n// after\nint safeEnd = Math.min(end, frameFile.numFrames());\nif (safeEnd < start) { throw new IllegalArgumentException(\"empty range\"); }\nnew ReadableFileFrameChannel(frameFile, start, safeEnd, deserializer);","handlingStrategy":"validation","validationCode":"if (frameFile.numFrames() == 0 || endFrame > frameFile.numFrames() || startFrame < 0 || startFrame > endFrame) {\n  throw new IllegalArgumentException(\"invalid frame range [\" + startFrame + \", \" + endFrame + \") numFrames=\" + frameFile.numFrames());\n}","typeGuard":null,"tryCatchPattern":"try { new ReadableFileFrameChannel(frameFile, start, end, deser); } catch (IllegalArgumentException e) { /* clamp range and retry */ }","preventionTips":["Always clamp ranges with Math.min(end, frameFile.numFrames())","Remember the range is half-open [startFrame, endFrame)","Compute ranges from the same FrameFile instance you open the channel with"],"tags":["frames","range-validation","iae"],"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-14T11:17:12.474Z"}