elastic/elasticsearch · error · IllegalArgumentException

distances array must have length >= 4, but was: {}

Error message

distances array must have length >= 4, but was: {}

What it means

Thrown by squareDistanceBulk (byte, offset variant) when distances.length < 4: the bulk operation writes four floats (one per candidate) into the output array, so it needs at least four slots beyond the offset. The message reports the actual length so you can see how short it is.

Source

Thrown at libs/simdvec/src/main/java/org/elasticsearch/simdvec/ESVectorUtil.java:297

    /** Returns the sum of squared differences of the two byte vectors over a sub-range. */
    public static float squareDistance(byte[] a, byte[] b, int offset, int length) {
        if (a.length != b.length) {
            throw new IllegalArgumentException("vector dimensions incompatible: " + a.length + "!= " + b.length);
        }
        Objects.checkFromIndexSize(offset, length, a.length);
        return IMPL.squareDistance(a, b, offset, length);
    }

    /**
     * Bulk computation of square distances from a byte query vector to four byte candidate vectors.
     */
    public static void squareDistanceBulk(byte[] q, byte[] v0, byte[] v1, byte[] v2, byte[] v3, int distancesOffset, float[] distances) {
        if (q.length != v0.length || q.length != v1.length || q.length != v2.length || q.length != v3.length) {
            throw new IllegalArgumentException("vector dimensions incompatible");
        }
        if (distances.length < 4) {
            throw new IllegalArgumentException("distances array must have length >= 4, but was: " + distances.length);
        }
        if (distancesOffset < 0 || distancesOffset > distances.length - 4) {
            throw new IllegalArgumentException("distancesOffset must be between 0 and distances.length - 4");
        }
        IMPL.squareDistanceBulk(q, 0, v0, v1, v2, v3, distancesOffset, distances, q.length);
    }

    /**
     * Bulk computation of square distances from a sub-range of a byte query vector to four byte candidate vectors.
     */
    public static void squareDistanceBulk(
        byte[] q,
        int qOffset,
        int length,
        byte[] v0,
        byte[] v1,
        byte[] v2,
        byte[] v3,

View on GitHub (pinned to db6a809a66)

Solutions

  1. Allocate the distances buffer as new float[4] (or larger if you intend to also use distancesOffset).
  2. Centralize scratch-buffer allocation in a factory that guarantees minimum length 4 for bulk scorers.
  3. Assert distances.length >= 4 once when the scorer is constructed, not on every call.

Example fix

// before
float[] dist = new float[3];
ESVectorUtil.squareDistanceBulk(q, v0, v1, v2, v3, 0, dist);

// after
float[] dist = new float[4];
ESVectorUtil.squareDistanceBulk(q, v0, v1, v2, v3, 0, dist);
Defensive patterns

Strategy: validation

Validate before calling

if (distances.length < 4) {
    throw new IllegalArgumentException("squareDistanceBulk needs distances.length >= 4, got " + distances.length);
}
ESVectorUtil.squareDistanceBulk(q, v0, v1, v2, v3, distancesOffset, distances);

Try / catch

try {
    ESVectorUtil.squareDistanceBulk(q, v0, v1, v2, v3, off, dist);
} catch (IllegalArgumentException e) {
    throw new QueryException("squareDistanceBulk distances buffer too small", e);
}

Prevention

When it happens

Trigger: Calling squareDistanceBulk with a float[] distances argument whose length is less than 4. Common when reusing a smaller scratch buffer from a non-bulk code path or allocating dist with the wrong constant.

Common situations: Scratch buffer sized for a single result (length 1) and reused for a bulk call; allocation typo (e.g. new float[3]); buffer pool that hands out arrays of varying capacity.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/cae8753e412a0fea. Report an issue: GitHub.