{"record":{"id":"adfc41d963e8b56a","repo":"elastic/elasticsearch","slug":"pitch-needs-to-be-at-least","errorCode":null,"errorMessage":"Pitch needs to be at least {}","messagePattern":"Pitch needs to be at least (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/native/src/main/java/org/elasticsearch/nativeaccess/SimdVecLibrary.java","lineNumber":193,"sourceCode":"        @VectorSegment(countParam = \"length\", elementBits = Byte.SIZE) MemorySegment query,\n        int length,\n        int pitch,\n        @VectorSegment(countParam = \"count\", elementBits = Integer.SIZE, aligned = true) MemorySegment offsets,\n        int count,\n        @VectorSegment(countParam = \"count\", elementBits = Float.SIZE, aligned = true) MemorySegment scores\n    );\n\n    public void dotProductI7uBulkWithOffsets(\n        MemorySegment dataset,\n        MemorySegment query,\n        int length,\n        int pitch,\n        MemorySegment offsets,\n        int count,\n        MemorySegment scores\n    ) {\n        if (pitch < length) {\n            throw new IllegalArgumentException(\"Pitch needs to be at least \" + length);\n        }\n        assert validateBulkOffsets(dataset, offsets, count, pitch, length);\n        dotProductI7uBulkWithOffsets_raw(dataset, query, length, pitch, offsets, count, scores);\n    }\n\n    @Function(\"vec_doti7u_bulk_sparse\")\n    @Critical(fallbackAdapter = Critical.UnsupportedFallback.class)\n    protected abstract void dotProductI7uBulkSparse_raw(\n        @VectorSegment(countParam = \"count\", elementBits = Long.SIZE, aligned = true) MemorySegment addresses,\n        @VectorSegment(countParam = \"length\", elementBits = Byte.SIZE) MemorySegment query,\n        int length,\n        int count,\n        @VectorSegment(countParam = \"count\", elementBits = Float.SIZE, aligned = true) MemorySegment scores\n    );\n\n    public void dotProductI7uBulkSparse(MemorySegment addresses, MemorySegment query, int length, int count, MemorySegment scores) {\n        assert validateBulkSparse(addresses, count);\n        dotProductI7uBulkSparse_raw(addresses, query, length, count, scores);","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/native/src/main/java/org/elasticsearch/nativeaccess/SimdVecLibrary.java#L175-L211","documentation":"Thrown by dotProductI7uBulkWithOffsets() in SimdVecLibrary before dispatching to the native vec_doti7u_bulk_with_offsets kernel. The pitch parameter is the byte stride between consecutive rows in the dataset MemorySegment (row-major layout). For 7-bit unsigned integer vectors, each element is 1 byte, so a row of 'length' elements occupies 'length' bytes. Pitch must be >= length so rows do not overlap. This is a hard pre-check (IllegalArgumentException), while the subsequent assert validateBulkOffsets is a secondary bounds check enabled only with -ea.","triggerScenarios":"Calling dotProductI7uBulkWithOffsets(dataset, query, length, pitch, offsets, count, scores) where pitch < length. The dataset is a row-major matrix indexed by offsets[i] * pitch. Pitch represents the byte distance from the start of one row to the next.","commonSituations":"Caller computed pitch in elements instead of bytes (though for byte-sized types these are the same); caller used a padded matrix but passed the unpadded length as pitch by mistake; caller confused the 'length' (number of vector elements per row) with 'pitch' (byte stride including padding).","solutions":["Ensure pitch >= length. For a tightly-packed i7u dataset (no per-row padding), set pitch = length.","If the dataset has per-row alignment padding, set pitch to the actual byte stride including padding.","Verify the offsets array is computed as row_start_byte / pitch (each offsets[i] indexes into the matrix by row unit).","Double-check that 'length' is the number of vector elements (7-bit unsigned integers), not the byte count of the query."],"exampleFix":"// before: pitch smaller than row width\nint length = 128;\nint pitch = 100; // BUG: must be >= 128\nlib.dotProductI7uBulkWithOffsets(dataset, query, length, pitch, offsets, count, scores);\n\n// after: pitch equals row width for tightly-packed data\nint pitch = length; // 128 bytes = 128 elements * 1 byte/element\nlib.dotProductI7uBulkWithOffsets(dataset, query, length, pitch, offsets, count, scores);","handlingStrategy":"validation","validationCode":"// Validate pitch before calling dotProductI7uBulkWithOffsets.\n// For i7u vectors, each element is 1 byte, so row width = length bytes.\nvoid safeDotProductI7uBulkWithOffsets(SimdVecLibrary lib, MemorySegment dataset, MemorySegment query,\n        int length, int pitch, MemorySegment offsets, int count, MemorySegment scores) {\n    if (pitch < length) {\n        throw new IllegalArgumentException(\n            \"pitch (\" + pitch + \") must be >= length (\" + length + \") for i7u vectors\");\n    }\n    lib.dotProductI7uBulkWithOffsets(dataset, query, length, pitch, offsets, count, scores);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always compute pitch from the element size: for byte-sized types (i7u, i8), pitch >= length in bytes.","For tightly-packed datasets, set pitch = length (no padding).","Verify offsets[i] * pitch + length does not exceed dataset.byteSize() before calling.","Use a helper function that derives pitch from the data layout to avoid manual miscalculation."],"tags":["vector","simd","validation","argument","i7u","dot-product"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}