prestodb/presto · error · IllegalArgumentException
length %d must be a multiple of 16.
Error message
length %d must be a multiple of 16.
What it means
BlockUtil.getNum128Integers converts a byte length into a count of 128-bit integers (length / 16). Because the conversion only works for whole 128-bit units, the length must be an exact multiple of 16 (SIZE_OF_LONG * 2); otherwise the operation would silently truncate partial data, so the library throws this IllegalArgumentException. It is a strict alignment requirement on the input length.
Source
Thrown at presto-common/src/main/java/com/facebook/presto/common/block/BlockUtil.java:285
newIsNull[desiredLength - 1] = true;
return newIsNull;
}
static int[] appendNullToOffsetsArray(int[] offsets, int offsetBase, int positionCount)
{
checkArrayRange(offsets, offsetBase, positionCount + 1);
int desiredLength = offsetBase + positionCount + 2;
int[] newOffsets = Arrays.copyOf(offsets, desiredLength);
newOffsets[desiredLength - 1] = newOffsets[desiredLength - 2];
return newOffsets;
}
public static int getNum128Integers(int length)
{
int num128Integers = length / SIZE_OF_LONG / 2;
if (num128Integers * SIZE_OF_LONG * 2 != length) {
throw new IllegalArgumentException(format("length %d must be a multiple of 16.", length));
}
return num128Integers;
}
/**
* Returns the input blocks array if all blocks are already loaded, otherwise returns a new blocks array with all blocks loaded
*/
static Block[] ensureBlocksAreLoaded(Block[] blocks)
{
for (int i = 0; i < blocks.length; i++) {
Block loaded = blocks[i].getLoadedBlock();
if (loaded != blocks[i]) {
// Transition to new block creation mode after the first newly loaded block is encountered
Block[] loadedBlocks = blocks.clone();
loadedBlocks[i++] = loaded;
for (; i < blocks.length; i++) {
loadedBlocks[i] = blocks[i].getLoadedBlock();
}View on GitHub (pinned to 55bb57d202)
Solutions
- Ensure the input length is a multiple of 16 before calling: round/slice the buffer to whole 128-bit units.
- Verify the byte width of the values being processed is 16 (two longs); if the data is narrower, use the correct vectorized helper for that width.
- Check slicing logic upstream (offsets/lengths math) for misalignment; add an assertion length % 16 == 0 where buffers are constructed.
Example fix
// before int n = BlockUtil.getNum128Integers(sliceLength); // throws when sliceLength % 16 != 0 // after checkArgument(sliceLength % 16 == 0, "length must be a multiple of 16, got %s", sliceLength); int n = BlockUtil.getNum128Integers(sliceLength);
Defensive patterns
Strategy: validation
Validate before calling
if (length < 0 || length % 16 != 0) {
throw new IllegalArgumentException("length must be a non-negative multiple of 16: " + length);
}
int num128Integers = BlockUtil.getNum128Integers(length); Type guard
boolean is16ByteAligned(int length) {
return length >= 0 && (length & 15) == 0;
} Try / catch
try {
n = BlockUtil.getNum128Integers(length);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("must be a multiple of 16")) {
throw new IllegalStateException("misaligned buffer passed to 128-bit path: " + length, e);
} else {
throw e;
}
} Prevention
- Assert length % 16 == 0 wherever fixed-width 16-byte buffers are sliced.
- Verify element byte widths before routing data into 128-bit vectorized helpers.
- Recheck slicing/offset math after changing fixed-width type sizes.
- Add round-trip tests that slice buffers at every supported width.
When it happens
Trigger: Calling getNum128Integers with a length that is not divisible by 16, e.g. getNum128Integers(20) or getNum128Integers(0-modulo-8-only sizes like 8 or 24). Usually reached from fixed-width block/vectorized hashing paths where a buffer was sliced to a non-aligned boundary.
Common situations: Custom slice/buffer arithmetic producing misaligned lengths (off-by-N slicing of a fixed-width value buffer); a custom block type whose element size is not 16 bytes being fed into a 128-bit vectorized routine; version changes where value widths changed but the hash path assumed 16-byte alignment.
Related errors
- New column does not have same number of rows as old column
- Declared positions (%s) does not match block %s's number of
- Offset is not monotonically ascending. offsets[%s]=%s, offse
- A null map must have zero entries
- Number of fields in RowBlock must be positive
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/759ff3823c3974d2.
Report an issue: GitHub.