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

AVLTreeDigest.quantile(q) computes the value at quantile q. The contract requires q in the closed interval [0,1]; anything below 0 or above 1 throws IllegalArgumentException immediately, before any centroid lookup. With empty data it returns NaN, with a single centroid it returns that mean.

Source

Thrown at libs/tdigest/src/main/java/org/elasticsearch/tdigest/AVLTreeDigest.java:302

            }
            r += a.count();

            // for the last element, assume right width is same as left
            if (x < b.mean() + right) {
                return (r + b.count() * interpolate(x, b.mean() - right, b.mean() + right)) / count;
            }
            return 1;
        }
    }

    /**
     * @param q The quantile desired.  Can be in the range [0,1].
     * @return The minimum value x such that we think that the proportion of samples is &le; x is q.
     */
    @Override
    public double quantile(double q) {
        if (q < 0 || q > 1) {
            throw new IllegalArgumentException("q should be in [0,1], got " + q);
        }

        AVLGroupTree values = summary;
        if (values.isEmpty()) {
            // no centroids means no data, no way to get a quantile
            return Double.NaN;
        } else if (values.size() == 1) {
            // with one data point, all quantiles lead to Rome
            return values.iterator().next().mean();
        }

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

        // deal with min and max as a special case singletons
        if (index <= 0) {
            return min;
        }

View on GitHub (pinned to db6a809a66)

Solutions

  1. Convert percent-in to fraction: divide by 100.0 before calling quantile
  2. Clamp q to [0,1]: Math.max(0.0, Math.min(1.0, q))
  3. Reject or skip when Double.isNaN(q)
  4. Audit the upstream arithmetic that produced q

Example fix

// before
double q = percentile; // percentile = 99.0
double v = digest.quantile(q); // throws
// after
double q = Math.max(0.0, Math.min(1.0, percentile / 100.0));
double v = digest.quantile(q);
Defensive patterns

Strategy: validation

Validate before calling

double q = percentile / 100.0;
if (Double.isNaN(q) || q < 0.0 || q > 1.0) {
    throw new IllegalArgumentException("quantile out of range: " + q);
}
double v = 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) { /* clamp or skip */ }

Prevention

When it happens

Trigger: Calling tdigest.quantile(q) where q is negative, > 1, NaN, or otherwise outside [0,1]. Common when q is computed from a ratio that can exceed the unit interval or from user-supplied percentiles divided incorrectly.

Common situations: Passing a percentile like 99 meaning 99% instead of 0.99; dividing by zero or null; computing q from an unbounded aggregation; feeding NaN from an upstream computation.

Related errors


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