elastic/elasticsearch · error · IllegalArgumentException

q should be in [0,1], got ${q}

Error message

q should be in [0,1], got ${q}

What it means

MergingDigest.quantile(q) is the MergingTDigest variant of the quantile lookup. It enforces the same [0,1] contract as AVLTreeDigest and throws IllegalArgumentException for any q outside it before merging new values and reading centroids. Behaviour for empty/single-centroid digests mirrors AVLTreeDigest.

Source

Thrown at libs/tdigest/src/main/java/org/elasticsearch/tdigest/MergingDigest.java:487

                weightSoFar += weight.get(i);
                left = right;
            }

            // for the last element, assume right width is same as left
            int lastOffset = lastUsedCell - 1;
            double right = (mean.get(lastOffset) - mean.get(lastOffset - 1)) / 2;
            if (x < mean.get(lastOffset) + right) {
                return (weightSoFar + weight.get(lastOffset) * interpolate(x, mean.get(lastOffset) - right, mean.get(lastOffset) + right))
                    / size();
            }
            return 1;
        }
    }

    @Override
    public double quantile(double q) {
        if (q < 0 || q > 1) {
            throw new IllegalArgumentException("q should be in [0,1], got " + q);
        }
        mergeNewValues();

        if (lastUsedCell == 0) {
            // no centroids means no data, no way to get a quantile
            return Double.NaN;
        } else if (lastUsedCell == 1) {
            // with one data point, all quantiles lead to Rome
            return mean.get(0);
        }

        // we know that there are at least two centroids now
        int n = lastUsedCell;

        // if values were stored in a sorted array, index would be the offset we are interested in
        final double index = q * totalWeight;

        // beyond the boundaries, we return min or max

View on GitHub (pinned to db6a809a66)

Solutions

  1. Pass q in [0,1]; convert percentages by dividing by 100
  2. Clamp: Math.max(0.0, Math.min(1.0, q))
  3. Guard against NaN before calling

Example fix

// before
double v = digest.quantile(p); // p = 1.5
// after
double v = digest.quantile(Math.max(0.0, Math.min(1.0, p)));
Defensive patterns

Strategy: validation

Validate before calling

if (Double.isNaN(q) || q < 0.0 || q > 1.0) {
    throw new IllegalArgumentException("quantile out of range: " + q);
}
return digest.quantile(q);

Type guard

static boolean isValidQuantile(double q) {
    return !Double.isNaN(q) && q >= 0.0 && q <= 1.0;
}

Try / catch

try { digest.quantile(q); }
catch (IllegalArgumentException e) { /* log and degrade */ }

Prevention

When it happens

Trigger: Calling mergingDigest.quantile(q) with q < 0, q > 1, or q = NaN. This variant first validates then calls mergeNewValues(), so the throw happens before any merging work.

Common situations: Same family of bugs as AVLTreeDigest: percent-vs-fraction confusion, unbounded ratios, NaN propagation, off-by-one in array indexing that produces the quantile argument.

Related errors


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