{"record":{"id":"5aa0e86436e22e16","repo":"aeron-io/aeron","slug":"term-offset-not-multiple-of-frame-alignment-termoffset","errorCode":null,"errorMessage":"term offset not multiple of FRAME_ALIGNMENT: ${termOffset}","messagePattern":"term offset not multiple of FRAME_ALIGNMENT: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"aeron-client/src/main/java/io/aeron/ChannelUriStringBuilder.java","lineNumber":843,"sourceCode":"     * Set the offset within a term at which a publication will start. This when combined with the term id can establish\n     * a starting position.\n     *\n     * @param termOffset within a term at which a publication will start.\n     * @return this for a fluent API.\n     * @see CommonContext#TERM_OFFSET_PARAM_NAME\n     */\n    public ChannelUriStringBuilder termOffset(final Integer termOffset)\n    {\n        if (null != termOffset)\n        {\n            if ((termOffset < 0 || termOffset > TERM_MAX_LENGTH))\n            {\n                throw new IllegalArgumentException(\"term offset not in range 0-1g: \" + termOffset);\n            }\n\n            if (0 != (termOffset & (FRAME_ALIGNMENT - 1)))\n            {\n                throw new IllegalArgumentException(\"term offset not multiple of FRAME_ALIGNMENT: \" + termOffset);\n            }\n        }\n\n        this.termOffset = termOffset;\n        return this;\n    }\n\n    /**\n     * Set the termOffset value to be what is in the {@link ChannelUri} which may be null.\n     *\n     * @param channelUri to read the value from.\n     * @return this for a fluent API.\n     * @see CommonContext#TERM_OFFSET_PARAM_NAME\n     */\n    public ChannelUriStringBuilder termOffset(final ChannelUri channelUri)\n    {\n        final String termOffsetValue = channelUri.get(TERM_OFFSET_PARAM_NAME);\n        if (null == termOffsetValue)","sourceCodeStart":825,"sourceCodeEnd":861,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-client/src/main/java/io/aeron/ChannelUriStringBuilder.java#L825-L861","documentation":"ChannelUriStringBuilder.termOffset(Integer) requires the offset to be a multiple of FRAME_ALIGNMENT (32 bytes). Offsets must point at message frame boundaries inside the term buffer; an unaligned offset would corrupt frame parsing. This is thrown after the range check and before the value is stored.","triggerScenarios":"Calling termOffset() with any value not divisible by 32, e.g. termOffset(100), or URIs with 'term-offset=50'.","commonSituations":"Deriving offsets from raw stream positions without aligning to frame boundaries; manually tuning replay/resume points; constructing URIs from arbitrary recorded positions.","solutions":["Round the offset down to the frame alignment: offset & ~(FRAME_ALIGNMENT - 1) with FRAME_ALIGNMENT=32","Use initialPosition(...) with an already-aligned position so the offset is derived correctly","Verify that whatever produced the offset aligns positions to 32-byte frames"],"exampleFix":"// before\nbuilder.termOffset(100); // not multiple of 32\n// after\nbuilder.termOffset(96); // 100 & ~31 == 96","handlingStrategy":"validation","validationCode":"int FRAME_ALIGNMENT = 32;\nif (termOffset != null && (termOffset & (FRAME_ALIGNMENT - 1)) != 0) termOffset &= ~(FRAME_ALIGNMENT - 1);","typeGuard":null,"tryCatchPattern":"try { builder.termOffset(offset); } catch (IllegalArgumentException e) { builder.termOffset(offset & ~31); }","preventionTips":["Always align offsets to 32-byte frame boundaries before use","Derive offsets from Aeron positions rather than raw stream counters","Document that FRAME_ALIGNMENT is fixed at 32 for standard channels"],"tags":["aeron","uri-validation","alignment"],"backgroundTag":"invalid-argument-value","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}