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

  1. Round the position down (or use the exact recorded position) to a multiple of FRAME_ALIGNMENT (32): position & ~(FRAME_ALIGNMENT - 1).
  2. 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.
  3. 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

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


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)