apache/druid · error · IllegalArgumentException

endFrame[%,d] > numFrames[%,d]

Error message

endFrame[%,d] > numFrames[%,d]

What it means

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.

Source

Thrown at processing/src/main/java/org/apache/druid/frame/channel/ReadableFileFrameChannel.java:67

      final int endFrame,
      final WireTransferable.ConcreteDeserializer deserializer
  )
  {
    this.frameFile = frameFile;
    this.deserializer = deserializer;
    this.currentFrame = startFrame;
    this.endFrame = endFrame;

    if (startFrame < 0) {
      throw new IAE("startFrame[%,d] < 0", startFrame);
    }

    if (startFrame > endFrame) {
      throw new IAE("startFrame[%,d] > endFrame[%,d]", startFrame, endFrame);
    }

    if (endFrame > frameFile.numFrames()) {
      throw new IAE("endFrame[%,d] > numFrames[%,d]", endFrame, frameFile.numFrames());
    }
  }

  public ReadableFileFrameChannel(
      final FrameFile frameFile,
      final WireTransferable.ConcreteDeserializer deserializer
  )
  {
    this(frameFile, 0, frameFile.numFrames(), deserializer);
  }

  @Override
  public boolean isFinished()
  {
    return currentFrame == endFrame;
  }

  @Override

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Check frameFile.numFrames() before constructing and clamp endFrame to it.
  2. Verify the frame range was computed against the same FrameFile instance/version that is being opened.
  3. Use the two-argument constructor ReadableFileFrameChannel(frameFile, deserializer) to read all frames and avoid manual range math.
  4. Fix off-by-one logic: the range is [startFrame, endFrame), so endFrame may equal numFrames but not exceed it.

Example fix

// before
new ReadableFileFrameChannel(frameFile, start, end, deserializer);
// after
int safeEnd = Math.min(end, frameFile.numFrames());
if (safeEnd < start) { throw new IllegalArgumentException("empty range"); }
new ReadableFileFrameChannel(frameFile, start, safeEnd, deserializer);
Defensive patterns

Strategy: validation

Validate before calling

if (frameFile.numFrames() == 0 || endFrame > frameFile.numFrames() || startFrame < 0 || startFrame > endFrame) {
  throw new IllegalArgumentException("invalid frame range [" + startFrame + ", " + endFrame + ") numFrames=" + frameFile.numFrames());
}

Try / catch

try { new ReadableFileFrameChannel(frameFile, start, end, deser); } catch (IllegalArgumentException e) { /* clamp range and retry */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/2a9d4eb40b32aa17. Report an issue: GitHub.