aeron-io/aeron · error · IllegalArgumentException
length must be positive
Error message
length must be positive
What it means
After clamping the requested length against what actually exists (stopPosition - fromPosition), if the resulting replayLength is negative the requested replay range is invalid (e.g. fromPosition beyond the recording's stopPosition with a positive length), so IllegalArgumentException is thrown.
Solutions
- Clamp fromPosition to at most the recording's current stopPosition before requesting replay.
- Fetch a fresh RecordingSummary/recording descriptor instead of a cached stopPosition.
- Compute the replay window as [fromPosition, min(stopPosition, fromPosition+length)] client-side.
- If replaying a live recording, poll for progress and adjust the window.
Example fix
// before long from = stopPosition + 1024; // beyond end reader = new RecordingReader(summary, dir, from, length); // after long from = Math.min(requestedFrom, summary.stopPosition); reader = new RecordingReader(summary, dir, from, length);
Defensive patterns
Strategy: validation
Validate before calling
long stop = summary.stopPosition != Aeron.NULL_POSITION ? summary.stopPosition : Long.MAX_VALUE; if (fromPosition > stop) fromPosition = stop; // clamp before replay
Try / catch
try { replay(recId, from, len); }
catch (IllegalArgumentException e) {
if (e.getMessage().equals("length must be positive")) {
from = Math.min(from, fetchStopPosition(recId));
}
} Prevention
- Fetch a fresh recording summary rather than caching stopPosition.
- Clamp replay windows to [0, currentStopPosition].
- For live recordings, adapt the window as the recording progresses.
When it happens
Trigger: Requesting a replay whose fromPosition lies after the recording's stopPosition so that stopPosition - fromPosition plus the requested length yields a negative replayLength; passing NULL_LENGTH while fromPosition already exceeds stopPosition.
Common situations: Replaying a still-active recording with a position past the current stop; using a stale stopPosition snapshot; off-by-one in client-computed replay windows; replaying after the recording was truncated/rolled back.
Related errors
- invalid position: " + position
- invalid length: " + length
- fromPosition + " position not aligned to valid fragment
- failed to open recording segment file " + segmentFileName
- errorCode (variable)
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/e7abd4241cf70c36.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/RecordingReader.java:86
if (length < NULL_LENGTH)
{
throw new IllegalArgumentException("invalid length: " + length);
}
this.archiveDir = archiveDir;
this.termLength = recordingSummary.termBufferLength;
this.segmentLength = recordingSummary.segmentFileLength;
this.recordingId = recordingSummary.recordingId;
final long startPosition = recordingSummary.startPosition;
final long fromPosition = position == NULL_POSITION ? startPosition : position;
final long maxLength = recordingSummary.stopPosition != NULL_POSITION ?
recordingSummary.stopPosition - fromPosition : Long.MAX_VALUE - fromPosition;
final long replayLength = length == NULL_LENGTH ? maxLength : Math.min(length, maxLength);
if (replayLength < 0)
{
throw new IllegalArgumentException("length must be positive");
}
final int positionBitsToShift = LogBufferDescriptor.positionBitsToShift(termLength);
final long startTermBasePosition = startPosition - (startPosition & (termLength - 1));
final int segmentOffset = (int)(fromPosition - startTermBasePosition) & (segmentLength - 1);
final int termId = ((int)(fromPosition >> positionBitsToShift) + recordingSummary.initialTermId);
segmentFilePosition = segmentFileBasePosition(startPosition, fromPosition, termLength, segmentLength);
openRecordingSegment();
termOffset = (int)(fromPosition & (termLength - 1));
termBaseSegmentOffset = segmentOffset - termOffset;
termBuffer = new UnsafeBuffer(mappedSegmentBuffer, termBaseSegmentOffset, termLength);
if (fromPosition > startPosition &&
(DataHeaderFlyweight.termOffset(termBuffer, termOffset) != termOffset ||
DataHeaderFlyweight.termId(termBuffer, termOffset) != termId ||
DataHeaderFlyweight.streamId(termBuffer, termOffset) != recordingSummary.streamId))View on GitHub (pinned to 6d60124e15)