prestodb/presto · error · PrestoException
INVALID_FUNCTION_ARGUMENT
INVALID_FUNCTION_ARGUMENT
Error message
Percentile must be between 0 and 1
What it means
getValidPercentile parses the percentile fraction string and validates it lies in [0, 1] before scaling to a percentage for Pinot's PERCENTILEEST aggregation. A fraction outside that range cannot be converted to a valid percentile, so the connector throws PrestoException with INVALID_FUNCTION_ARGUMENT.
Source
Thrown at presto-pinot-toolkit/src/main/java/com/facebook/presto/pinot/query/PinotQueryGenerator.java:439
// rsd = 1.106 / sqrt(2^(log2m))
// So:
// log2m = 2 * log(1.106 / rsd) / log(2)
int log2m = (int) (2 * Math.log(1.106 / standardError) / Math.log(2));
if (log2m < 1) {
throw new PinotException(
PINOT_UNSUPPORTED_EXPRESSION,
Optional.empty(),
format("Cannot handle approx_distinct, the log2m generated from error is %d from input %s (function %s)", log2m, standardErrorString, aggregation));
}
return format("DISTINCTCOUNTHLL(%s, %d)", selection, log2m);
}
private int getValidPercentile(String fraction)
{
try {
double percent = Double.parseDouble(fraction);
if (percent < 0 || percent > 1) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Percentile must be between 0 and 1");
}
percent = percent * 100.0;
if (percent == Math.floor(percent)) {
return (int) percent;
}
}
catch (NumberFormatException ne) {
// Skip
}
return -1;
}
@Override
public PinotQueryGeneratorContext visitAggregation(AggregationNode node, PinotQueryGeneratorContext contextIn)
{
List<AggregationColumnNode> aggregationColumnNodes = computeAggregationNodes(node);
// Make two passes over the aggregationColumnNodes: In the first pass identify all the variables that will be usedView on GitHub (pinned to 55bb57d202)
Solutions
- Pass the percentile as a fraction between 0 and 1, e.g. percentile(latency, 0.95) instead of percentile(latency, 95).
- Normalize programmatic inputs before building the query: if the value > 1, divide by 100.
- If the value should legitimately exceed 1, it is not a percentile; use a different aggregation.
Example fix
// before SELECT percentile(latency, 95) FROM metrics; // after SELECT percentile(latency, 0.95) FROM metrics;
Defensive patterns
Strategy: validation
Validate before calling
double p = Double.parseDouble(percentileInput);
if (p < 0 || p > 1) {
throw new IllegalArgumentException("Percentile must be between 0 and 1, got: " + p);
} Prevention
- Always express percentiles as fractions (0.95, not 95).
- Normalize p>1 inputs by dividing by 100 in query builders.
- Add unit tests for percentile argument formatting.
When it happens
Trigger: Pushing a percentile aggregation (e.g. percentile(x, 1.5) or percentile(x, -0.1)) down to Pinot where the fraction string parses to a double outside 0..1.
Common situations: Hand-written SQL passing 0-100 style percentile values (e.g. 95 instead of 0.95); programmatic query builders formatting the percentile with the wrong scale; dashboards configured with 'p95' mapped to 95.
Related errors
- PINOT_QUERY_GENERATOR_FAILURE
- PINOT_UNSUPPORTED_EXPRESSION
- PINOT_UNSUPPORTED_EXPRESSION
- PINOT_UNSUPPORTED_EXPRESSION
- PINOT_UNSUPPORTED_EXPRESSION
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/cd530dd5b34d5057.
Report an issue: GitHub.