prestodb/presto · error · PrestoException
QUERY_PLANNING_TIMEOUT
QUERY_PLANNING_TIMEOUT
Error message
The query optimizer exceeded the timeout of %s.
What it means
Thrown by Optimizer.validateAndOptimizePlan when the current thread is interrupted between running plan optimizers, indicating the query planning phase exceeded its allowed time (query.analyzer-timeout / planning timeout). Presto interrupts the planning thread once the deadline passes, and the optimizer loop converts the interrupt into a QUERY_PLANNING_TIMEOUT failure.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/Optimizer.java:111
this.planChecker = requireNonNull(planChecker, "planChecker is null");
this.metadata = requireNonNull(metadata, "metadata is null");
this.variableAllocator = requireNonNull(variableAllocator, "variableAllocator is null");
this.idAllocator = requireNonNull(idAllocator, "idAllocator is null");
this.warningCollector = requireNonNull(warningCollector, "warningCollector is null");
this.statsCalculator = requireNonNull(statsCalculator, "statsCalculator is null");
this.costCalculator = requireNonNull(costCalculator, "costCalculator is null");
this.explain = explain;
}
public Plan validateAndOptimizePlan(PlanNode root, PlanStage stage)
{
validateIntermediatePlanWithRuntimeStats(root);
boolean enableVerboseRuntimeStats = SystemSessionProperties.isVerboseRuntimeStatsEnabled(session) || SystemSessionProperties.isVerbosePlannerRuntimeStatsEnabled(session);
if (stage.ordinal() >= OPTIMIZED.ordinal()) {
for (PlanOptimizer optimizer : planOptimizers) {
if (Thread.currentThread().isInterrupted()) {
throw new PrestoException(QUERY_PLANNING_TIMEOUT, String.format("The query optimizer exceeded the timeout of %s.", getQueryAnalyzerTimeout(session).toString()));
}
long start = System.nanoTime();
PlanOptimizerResult optimizerResult = optimizer.optimize(root, session, TypeProvider.viewOf(variableAllocator.getVariables()), variableAllocator, idAllocator, warningCollector);
requireNonNull(optimizerResult, format("%s returned a null plan", optimizer.getClass().getName()));
if (enableVerboseRuntimeStats || trackOptimizerRuntime(session, optimizer)) {
session.getRuntimeStats().addMetricValue(String.format("optimizer%sTimeNanos", getOptimizerNameForLog(optimizer)), NANO, System.nanoTime() - start);
}
TypeProvider types = TypeProvider.viewOf(variableAllocator.getVariables());
collectOptimizerInformation(optimizer, root, optimizerResult, types);
root = optimizerResult.getPlanNode();
}
}
if (stage.ordinal() >= OPTIMIZED_AND_VALIDATED.ordinal()) {
// make sure we produce a valid plan after optimizations run. This is mainly to catch programming errors
validateFinalPlanWithRuntimeStats(root);
}
View on GitHub (pinned to 55bb57d202)
Solutions
- Increase the query.analyzer-timeout (planning timeout) session or config property
- Simplify the query (reduce join/union fan-out, split into stages) so planning completes in time
- Check cluster CPU load; planning is CPU-bound and contention lengthens it
- Profile which optimizer pass is slow (verbose planner runtime stats) and disable/avoid the pattern that triggers it
Example fix
// before SET SESSION query_analyzer_timeout = '2m'; // after SET SESSION query_analyzer_timeout = '10m';
Defensive patterns
Strategy: try-catch
Validate before calling
Duration timeout = getQueryAnalyzerTimeout(session);
// estimate planning cost before submitting: column/join count heuristics
if (joinCount > 50 || columnCount > 1000) { /* warn or raise timeout */ } Try / catch
try { Plan p = planner.plan(...); } catch (PrestoException e) { if (e.getErrorCode() == QUERY_PLANNING_TIMEOUT.toErrorCode()) { /* increase timeout, simplify query, or resubmit */ } else throw e; } Prevention
- Set query.analyzer-timeout generously for large analytical queries
- Avoid thousand-way joins/unions; split queries into staged materializations
- Monitor optimizer runtime stats to find slow passes
- Keep cluster CPU headroom; planning is CPU-bound
When it happens
Trigger: A query whose logical/optimized plan takes longer than the analyzer timeout: running an optimizer detects Thread.currentThread().isInterrupted() and throws. Triggered via plan/getLogicalPlan/createPlan on very large or pathological queries with expensive optimizer passes.
Common situations: Huge SQL with thousands of joins/unions; queries over thousands of columns; low query.analyzer-timeout session setting; cluster under CPU starvation slowing planning.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- INDEX_LOADER_TIMEOUT
- QUERY_PLANNING_TIMEOUT
- GENERIC_INTERNAL_ERROR
- OPTIMIZER_TIMEOUT
- INVALID_TABLE_PROPERTY
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/571c49d24c7963d6.
Report an issue: GitHub.