prestodb/presto · error · PrestoException

GENERIC_INTERNAL_ERROR

GENERIC_INTERNAL_ERROR

Error message

Expect exactly 1 child PlanNode

What it means

TopNNode validates its plan structure invariants during construction and child replacement via checkCondition. When a TopNNode is built or rewritten with a number of children other than exactly one (a TopN operator always consumes a single input plan), the SPI throws GENERIC_INTERNAL_ERROR. This signals a broken optimizer rule or connector-provided plan, not a user query problem per se.

Source

Thrown at presto-spi/src/main/java/com/facebook/presto/spi/plan/TopNNode.java:162

    @Override
    public PlanNode replaceChildren(List<PlanNode> newChildren)
    {
        checkCondition(newChildren != null && newChildren.size() == 1, GENERIC_INTERNAL_ERROR, "Expect exactly 1 child PlanNode");
        return new TopNNode(getSourceLocation(), getId(), getStatsEquivalentPlanNode(), newChildren.get(0), count, orderingScheme, step);
    }

    private static void checkArgument(boolean condition, String message)
    {
        if (!condition) {
            throw new IllegalArgumentException(message);
        }
    }

    private static void checkCondition(boolean condition, ErrorCodeSupplier errorCode, String formatString, Object... args)
    {
        if (!condition) {
            throw new PrestoException(errorCode, format(formatString, args));
        }
    }
}

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Inspect the optimizer/rewriter rule that produced the TopNNode and ensure replaceChildren is called with exactly one child
  2. Verify the query works without the custom plugin/rule to isolate the faulty component
  3. Check for version mismatches between presto-spi and custom plugins and rebuild against the matching SPI
  4. If reproducible in stock Presto, file a bug with the query and EXPLAIN (VERBOSE) output

Example fix

// before
node.replaceChildren(rewrittenChildren);
// after
com.google.common.collect.ImmutableList<PlanNode> children = rewrittenChildren.stream().limit(1).collect(ImmutableList.toImmutableList());
node.replaceChildren(children); // TopNNode requires exactly one child
Defensive patterns

Strategy: validation

Validate before calling

if (children == null || children.size() != 1) {
    throw new IllegalStateException("TopNNode requires exactly 1 child, got " + (children == null ? "null" : children.size()));
}

Type guard

static boolean hasSingleChild(PlanNode node) {
    return node.getSources().size() == 1;
}

Try / catch

try {
    planRewriter.rewrite(topnNode);
} catch (PrestoException e) {
    if (e.getErrorCode().getCode() == StandardErrorCode.GENERIC_INTERNAL_ERROR.toErrorCode().getCode()) {
        LOG.error("Plan rewrite produced invalid TopNNode arity", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling TopNNode constructor or replaceChildren(...) with a child list of size 0 or >= 2, e.g. a custom optimizer/PlanRewriter that removes the source child or wraps TopN in multiple inputs.

Common situations: Custom PlanRewriter/Optimizer rules that mis-handle TopNNode; connector or engine version mismatch where a rule produces an unexpected plan shape; bugs in plan serialization/deserialization round-trips.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/4dd383c3587adfa2. Report an issue: GitHub.