aeron-io/aeron · error · IllegalArgumentException

difference greater than 2^31 - 1: termId=

Error message

difference greater than 2^31 - 1: termId=${termId} - initialTermId=${initialTermId}

What it means

Thrown by ChannelUriStringBuilder.validate() when a termId is provided together with an initialTermId, but termId - initialTermId is negative. Because Aeron term IDs are compared using wrapping 31-bit arithmetic, a negative raw difference is interpreted as a difference larger than 2^31 - 1, which cannot be represented. The builder rejects the URI rather than emitting a channel string the media driver would consider inconsistent.

Solutions

  1. Ensure termId >= initialTermId when both are set (accounting for 31-bit wrap: use Aeron's wrappingCompare semantics).
  2. Remove the explicit termId/termOffset/initialTermId set (use initialPosition instead) and let the builder derive consistent values.
  3. If wrapping across 2^31 is intended, choose termId = initialTermId + delta such that the delta fits in positive 31-bit range.

Example fix

// before
builder.initialTermId(100).termId(90).validate();
// after
builder.initialTermId(100).termId(190).validate();
Defensive patterns

Strategy: validation

Validate before calling

if (termId != null && initialTermId != null && (termId - initialTermId) < 0) { throw new IllegalArgumentException("termId must be >= initialTermId (31-bit wrap)"); }

Type guard

boolean hasConsistentTermIds(Integer termId, Integer initialTermId) { return termId == null || initialTermId == null || Integer.compareUnsigned(termId - initialTermId, 1 << 31) < 0; }

Try / catch

try { uri = builder.validate().build(); } catch (IllegalArgumentException e) { /* fall back to initialPosition-derived term params */ }

Prevention

When it happens

Trigger: Calling validate() (or build() paths that validate) after setting initialPosition(...) or explicitly setting termId via termId(Integer) with a smaller value than the set initialTermId, e.g. builder.initialTermId(1000).termId(999).validate().

Common situations: Manually computing term IDs from a recording position and mis-ordering them; replaying a recording whose initial term id exceeds the target term id; copying parameters from an existing channel URI and editing termId downward.

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/5e7c974ceab7aa67. Report an issue: GitHub.

Appendix: source

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

        if (CommonContext.UDP_MEDIA.equals(media) && (null == endpoint && null == controlEndpoint))
        {
            throw new IllegalArgumentException("either 'endpoint' or 'control' must be specified for UDP.");
        }

        final boolean anyNonNull = null != initialTermId || null != termId || null != termOffset;
        final boolean anyNull = null == initialTermId || null == termId || null == termOffset;
        if (anyNonNull)
        {
            if (anyNull)
            {
                throw new IllegalArgumentException(
                    "either all or none of the parameters ['initialTermId', 'termId', 'termOffset'] must be provided");
            }

            if (termId - initialTermId < 0)
            {
                throw new IllegalArgumentException(
                    "difference greater than 2^31 - 1: termId=" + termId + " - initialTermId=" + initialTermId);
            }

            if (null != termLength && termOffset > termLength)
            {
                throw new IllegalArgumentException("termOffset=" + termOffset + " > termLength=" + termLength);
            }
        }

        return this;
    }

    /**
     * Set the prefix for taking an additional action such as spying on an outgoing publication with "aeron-spy".
     *
     * @param prefix to be applied to the URI before the scheme.
     * @return this for a fluent API.
     * @see ChannelUri#SPY_QUALIFIER

View on GitHub (pinned to 6d60124e15)