aeron-io/aeron · error · IllegalArgumentException

invalid position= < 0

Error message

invalid position=${position} < 0

What it means

ChannelUriStringBuilder.initialPosition(position, initialTermId, termLength) computes the term offset from the given absolute position and rejects negative positions outright. Positions in Aeron are non-negative 64-bit stream offsets; a negative value indicates corrupted arithmetic or a wrongly decoded position. (A second check in the same method also requires frame alignment of 32 bytes.)

Solutions

  1. Fix the source of the position so it is never negative before calling initialPosition
  2. Validate the position and reject/repair negative values: if (position < 0) throw or reset to 0
  3. Also ensure the position is a multiple of FRAME_ALIGNMENT (32) or the next check will throw
  4. Use values from Aeron's own position APIs (publication.position(), image.position()) which are guaranteed non-negative

Example fix

// before
long pos = recordedPosition - basePosition; // can underflow
builder.initialPosition(pos, termId, termLength);
// after
long pos = Math.max(0, recordedPosition - basePosition);
pos &= ~(32 - 1); // align to FRAME_ALIGNMENT
builder.initialPosition(pos, termId, termLength);
Defensive patterns

Strategy: validation

Validate before calling

int FRAME_ALIGNMENT = 32;
if (position < 0) throw new IllegalArgumentException("position must be >= 0");
if ((position & (FRAME_ALIGNMENT - 1)) != 0) throw new IllegalArgumentException("position must be 32-byte aligned");

Try / catch

try { builder.initialPosition(pos, termId, termLength); } catch (IllegalArgumentException e) { /* clamp/recompute pos */ }

Prevention

When it happens

Trigger: Calling initialPosition(-1, termId, termLength), or passing a position computed by subtraction that underflowed, or one decoded from a packed long that lost its sign handling.

Common situations: Restoring a publication/replay position from persistent state where the stored value was corrupted; subtracting a larger position from a smaller one; casting unsigned 64-bit values above Long.MAX_VALUE to a negative long.

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 aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/5109010fb1235332. Report an issue: GitHub.

Appendix: source

Thrown at aeron-client/src/main/java/io/aeron/ChannelUriStringBuilder.java:1629

     */
    public Boolean spiesSimulateConnection()
    {
        return ssc;
    }

    /**
     * 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;
    }

    /**

View on GitHub (pinned to 6d60124e15)