{"record":{"id":"30cc93487c7cab9c","repo":"aeron-io/aeron","slug":"position-not-aligned-to-frame-alignment","errorCode":null,"errorMessage":" position not aligned to FRAME_ALIGNMENT","messagePattern":" position not aligned to FRAME_ALIGNMENT","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"aeron-client/src/main/java/io/aeron/Image.java","lineNumber":789,"sourceCode":"\n    private UnsafeBuffer activeTermBuffer(final long position)\n    {\n        return termBuffers[LogBufferDescriptor.indexByPosition(position, positionBitsToShift)];\n    }\n\n    private void validatePosition(final long position)\n    {\n        final long currentPosition = subscriberPosition.get();\n        final long limitPosition = (currentPosition - (currentPosition & termLengthMask)) + termLengthMask + 1;\n        if (position < currentPosition || position > limitPosition)\n        {\n            throw new IllegalArgumentException(\n                position + \" position out of range: \" + currentPosition + \"-\" + limitPosition);\n        }\n\n        if (0 != (position & (FRAME_ALIGNMENT - 1)))\n        {\n            throw new IllegalArgumentException(position + \" position not aligned to FRAME_ALIGNMENT\");\n        }\n    }\n\n    LogBuffers logBuffers()\n    {\n        return logBuffers;\n    }\n\n    void close()\n    {\n        finalPosition = subscriberPosition.getVolatile();\n        eosPosition = LogBufferDescriptor.endOfStreamPosition(logBuffers.metaDataBuffer());\n        isEos = finalPosition >= eosPosition;\n        isRevoked = LogBufferDescriptor.isPublicationRevoked(logBuffers.metaDataBuffer());\n        isClosed = true;\n    }\n\n    /**","sourceCodeStart":771,"sourceCodeEnd":807,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-client/src/main/java/io/aeron/Image.java#L771-L807","documentation":"Image.validatePosition() also throws this IllegalArgumentException when the supplied position is not a multiple of FRAME_ALIGNMENT (32 bytes). Aeron fragment positions must land on frame boundaries because positions encode offsets into term buffers where frames are frame-aligned.","triggerScenarios":"Calling Image.position(long) or similar with a value not aligned to FRAME_ALIGNMENT, e.g. a raw byte offset computed from fragment payload sizes rather than the value returned by fragment resolution functions.","commonSituations":"Computing positions by adding payload length instead of the aligned frame length; persisting positions after custom buffer arithmetic; tracking positions in an external consumer that loses alignment.","solutions":["Round the position down to the nearest FRAME_ALIGNMENT (32) multiple: position & ~(FRAME_ALIGNMENT - 1).","Only set positions obtained from Image.position() or fragment-consumed offsets, never hand-computed offsets.","Fix the offset arithmetic so frame header length is included and each frame is counted at its aligned size."],"exampleFix":"// before\nimage.position(offset);\n// after\nimage.position(offset & ~(FRAME_ALIGNMENT - 1)); // FRAME_ALIGNMENT = 32","handlingStrategy":"validation","validationCode":"if ((position & (FRAME_ALIGNMENT - 1)) != 0) { // FRAME_ALIGNMENT = 32\n    position &= ~(FRAME_ALIGNMENT - 1); // align down\n}","typeGuard":null,"tryCatchPattern":"try {\n    image.position(pos);\n} catch (IllegalArgumentException e) {\n    image.position(pos & ~(FRAME_ALIGNMENT - 1));\n}","preventionTips":["Track positions only as frame-aligned offsets returned by Aeron APIs.","Include frame header length in offset arithmetic, and align each frame to 32 bytes.","Assert alignment (position % 32 == 0) in debug builds when persisting positions."],"tags":["aeron","image","alignment","position-validation"],"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-16T04:17:20.429Z"}