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
- Ensure termId >= initialTermId when both are set (accounting for 31-bit wrap: use Aeron's wrappingCompare semantics).
- Remove the explicit termId/termOffset/initialTermId set (use initialPosition instead) and let the builder derive consistent values.
- 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
- Prefer initialPosition(long, int, int) over manually setting termId/termOffset/initialTermId
- Use Aeron's wrapping compare utilities when comparing term IDs
- Never hand-edit termId in copied URIs without recomputing the delta
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
- termOffset= > termLength=
- invalid control mode
- segment file length not a power of 2
- segment file length not in valid range
- AeronArchive.Context.messageRetryAttempts must be > 0, got:
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_QUALIFIERView on GitHub (pinned to 6d60124e15)