aeron-io/aeron · error · IllegalArgumentException
fromPosition + " position not aligned to valid fragment
Error message
fromPosition + " position not aligned to valid fragment
What it means
When replay starts after the recording's startPosition, the constructor checks that fromPosition points at a valid fragment by comparing the data header's termOffset, termId and streamId at that location. If fromPosition is not aligned to an actual fragment boundary (or belongs to a different stream), the reader closes its resources and throws IllegalArgumentException.
Solutions
- Resume replays only from positions previously reported as fragment boundaries (e.g. last position from a previous replay or recording progress).
- Ensure the streamId in the replay request matches the recorded stream.
- Use boundedReplay/replay with NULL_POSITION to start from the beginning instead of guessing offsets.
- Capture positions via the recording events/recording descriptor APIs rather than manual arithmetic.
Example fix
// before long resumeAt = lastKnownPosition + 123; // arbitrary offset // after long resumeAt = lastFragmentPosition; // position previously reported by replay/callbacks archive.startReplay(recordingId, resumeAt, Aeron.NULL_LENGTH, channel, streamId);
Defensive patterns
Strategy: validation
Validate before calling
// only resume from positions that were reported as fragment boundaries
if (resumePosition > startPosition && !positionsFromCallbacks.contains(resumePosition)) {
resumePosition = Aeron.NULL_POSITION; // fall back to start
} Try / catch
try { replay(recId, from, len); }
catch (IllegalArgumentException e) {
if (e.getMessage().endsWith("position not aligned to valid fragment")) {
replayFromLastKnownBoundary(recId);
}
} Prevention
- Track fragment-boundary positions from replay callbacks instead of guessing offsets.
- Never resume from a position obtained on a different streamId or recording.
- Store the last delivered position durably for resumable replays.
When it happens
Trigger: Requesting a replay from a position that was never a fragment start — an arbitrary byte offset, a position from a different recording/stream, or a padded/batch boundary rather than a fragment boundary.
Common situations: Client guessed a resume position instead of tracking the last received fragment position; reusing positions recorded from a different streamId; resuming after archive restart with mismatched term metadata.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- invalid position: " + position
- invalid length: " + length
- length must be positive
- 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/ba06f8fae8a22dbe.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/RecordingReader.java:107
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))
{
close();
throw new IllegalArgumentException(fromPosition + " position not aligned to valid fragment");
}
replayPosition = fromPosition;
replayLimit = fromPosition + replayLength;
}
/**
* {@inheritDoc}
*/
@Override
public void close()
{
closeRecordingSegment();
}
long replayPosition()
{
return replayPosition;View on GitHub (pinned to 6d60124e15)