aeron-io/aeron · error · IllegalArgumentException
invalid position= does not have frame alignment=
Error message
invalid position=${position} does not have frame alignment=${FRAME_ALIGNMENT} What it means
Thrown by ChannelUriStringBuilder's position setter (used when building channel URIs with a starting position, e.g. for term-buffer parameters). Aeron requires every publication position to be aligned to the frame alignment boundary (32 bytes, FRAME_ALIGNMENT). The builder rejects any position that is negative or not a multiple of FRAME_ALIGNMENT because such a position cannot correspond to a valid frame boundary in the log buffer.
Solutions
- Round the position down (or use the exact recorded position) to a multiple of FRAME_ALIGNMENT (32): position & ~(FRAME_ALIGNMENT - 1).
- Verify the source of the position: it must come from a valid frame boundary (e.g. an image position from Aeron), not an arbitrary byte offset.
- If you do not need to resume at a specific position, omit the position/initial-term-id parameters entirely and let Aeron assign them.
Example fix
// before builder.initialPosition(1234L, initialTermId, termLength); // not 32-byte aligned // after long aligned = 1234L & ~(32 - 1); // FRAME_ALIGNMENT = 32 builder.initialPosition(aligned, initialTermId, termLength);
Defensive patterns
Strategy: validation
Validate before calling
static final int FRAME_ALIGNMENT = 32;
void checkInitialPosition(long position) {
if (position < 0 || (position & (FRAME_ALIGNMENT - 1)) != 0) {
throw new IllegalArgumentException("position must be non-negative and a multiple of " + FRAME_ALIGNMENT + ": " + position);
}
} Type guard
boolean isFrameAligned(long position) { return position >= 0 && (position & (32 - 1)) == 0; } Try / catch
try {
builder.initialPosition(position, initialTermId, termLength);
} catch (IllegalArgumentException e) {
long aligned = Math.max(0, position & ~(32 - 1));
builder.initialPosition(aligned, initialTermId, termLength);
} Prevention
- Only use positions obtained from Aeron APIs (image positions, recording positions) — these are always frame-aligned.
- Align any computed position down to the 32-byte FRAME_ALIGNMENT before passing it.
- Add an assertion on position alignment in your configuration-loading code.
When it happens
Trigger: Calling ChannelUriStringBuilder.initialPosition(long position, int initialTermId, int termLength) (or the position-taking overload) with a position that is not divisible by 32 (FRAME_ALIGNMENT).
Common situations: Resuming a publication/subscription from a recorded position where the recorded value was the raw stream position rather than a frame-aligned one; hand-computed positions using wrong alignment (e.g. term length/2); ports of code from other messaging systems that use unaligned offsets.
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
- MTU not a multiple of FRAME_ALIGNMENT: mtu=
- counter id is not allocated, state
- counter id is negative
- counter id out of range: 0 - maxCounterId=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/12a53da810116f42.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-client/src/main/java/io/aeron/ChannelUriStringBuilder.java:1633
}
/**
* Initialise a channel for restarting a publication at a given position.
*
* @param position at which the publication should be started.
* @param initialTermId what which the stream would start.
* @param termLength for the stream.
* @return this for a fluent API.
*/
public ChannelUriStringBuilder initialPosition(final long position, final int initialTermId, final int termLength)
{
if (position < 0)
{
throw new IllegalArgumentException("invalid position=" + position + " < 0");
}
if (0 != (position & (FRAME_ALIGNMENT - 1)))
{
throw new IllegalArgumentException(
"invalid position=" + position + " does not have frame alignment=" + FRAME_ALIGNMENT);
}
final int bitsToShift = LogBufferDescriptor.positionBitsToShift(termLength);
this.initialTermId = initialTermId;
this.termId = LogBufferDescriptor.computeTermIdFromPosition(position, bitsToShift, initialTermId);
this.termOffset = (int)(position & (termLength - 1));
this.termLength = termLength;
return this;
}
/**
* Set the underlying OS send buffer length.
*
* @param socketSndbufLength parameter to be passed as SO_SNDBUF value.
* @return this for a fluent API.View on GitHub (pinned to 6d60124e15)