aeron-io/aeron · error · InvalidChannelException

term-offset= must be a multiple of FRAME_ALIGNMENT: channel=

Error message

term-offset={termOffset} must be a multiple of FRAME_ALIGNMENT: channel={channelUri}

What it means

Term offsets mark the start of protocol frames, so the term-offset URI parameter must be a multiple of FRAME_ALIGNMENT (32 bytes). Unaligned offsets would corrupt the log buffer framing and are rejected.

Solutions

  1. Round the offset down/up to a multiple of 32 using BitUtil.align or FrameDescriptor.isFrameAligned
  2. Remove the term-offset param if exact positioning is not required
  3. Compute the offset from a position as position & (termLength - 1), which is frame-aligned for aligned term lengths

Example fix

// before
"aeron:udp?endpoint=...:40456|term-offset=100"
// after
"aeron:udp?endpoint=...:40456|term-offset=128"
Defensive patterns

Strategy: validation

Validate before calling

if (termOffset % FrameAlignment.FRAME_ALIGNMENT != 0) throw new IllegalArgumentException("term-offset must be frame-aligned");

Try / catch

try { pub = aeron.addPublication(uri, streamId); } catch (InvalidChannelException e) { log.error("unaligned term-offset: {}", e.getMessage()); }

Prevention

When it happens

Trigger: A channel URI with a term-offset that is not a multiple of 32 (e.g. term-offset=100) when Aeron.addPublication resolves publication params.

Common situations: Deriving offsets by manual arithmetic without aligning; carrying over raw byte positions from a dump; typos in a hand-built URI.

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/3d4811ccd572b86e. Report an issue: GitHub.

Appendix: source

Thrown at aeron-driver/src/main/java/io/aeron/driver/PublicationParams.java:141

            params.termId = parseInt(termIdStr, TERM_ID_PARAM_NAME);
            params.termOffset = parseInt(termOffsetStr, TERM_OFFSET_PARAM_NAME);

            if (params.termOffset > params.termLength)
            {
                throw new InvalidChannelException(
                    TERM_OFFSET_PARAM_NAME + "=" + params.termOffset + " > " +
                    TERM_LENGTH_PARAM_NAME + "=" + params.termLength + ": channel=" + channelUri);
            }

            if (params.termOffset < 0 || params.termOffset > LogBufferDescriptor.TERM_MAX_LENGTH)
            {
                throw new InvalidChannelException(
                    TERM_OFFSET_PARAM_NAME + "=" + params.termOffset + " out of range: channel=" + channelUri);
            }

            if (!FrameDescriptor.isFrameAligned(params.termOffset))
            {
                throw new InvalidChannelException(
                    TERM_OFFSET_PARAM_NAME + "=" + params.termOffset +
                    " must be a multiple of FRAME_ALIGNMENT: channel=" + channelUri);
            }

            if (params.termId - params.initialTermId < 0)
            {
                throw new InvalidChannelException(
                    "difference greater than 2^31 - 1: " + INITIAL_TERM_ID_PARAM_NAME + "=" +
                    params.initialTermId + " when " + TERM_ID_PARAM_NAME + "=" + params.termId + " channel=" +
                    channelUri);
            }

            params.hasPosition = true;
        }
        else
        {
            params.initialTermId = BitUtil.generateRandomisedId();
            params.termId = params.initialTermId;

View on GitHub (pinned to 6d60124e15)