prestodb/presto · error · PrestoException
NOT_SUPPORTED
NOT_SUPPORTED
Error message
External function in join filter is not supported: %s
What it means
Presto's planner refuses to plan join queries whose join filter contains a call to an external (connector/plugin-provided) function. External functions cannot be safely evaluated in the distributed join-filter context, so CheckUnsupportedExternalFunctions walks the plan and rejects any JoinNode whose filter expression contains such a call.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/planner/sanity/CheckUnsupportedExternalFunctions.java:81
@Override
public Void visitAggregation(AggregationNode node, Void context)
{
checkState(
node.getAggregations().values().stream()
.noneMatch(
aggregation -> aggregation.getFilter().isPresent() ||
aggregation.getCall().accept(externalCallExpressionChecker, null)),
"Expect aggregation to be local");
node.getSource().accept(this, context);
return null;
}
@Override
public Void visitJoin(JoinNode node, Void context)
{
if (node.getFilter().isPresent() && node.getFilter().get().accept(externalCallExpressionChecker, null)) {
throw new PrestoException(NOT_SUPPORTED, format("External function in join filter is not supported: %s", node.getFilter().get()));
}
node.getSources().forEach(child -> child.accept(this, context));
return null;
}
@Override
public Void visitApply(ApplyNode node, Void context)
{
throw new IllegalStateException("Do not expect ApplyNode");
}
@Override
public Void visitLateralJoin(LateralJoinNode node, Void context)
{
throw new IllegalStateException("Do not expect ApplyNode");
}
}View on GitHub (pinned to 55bb57d202)
Solutions
- Move the external function call out of the join filter into a subquery/CTE that computes it before the join (e.g. precompute in a SELECT, then join on the result).
- Replace the external function with an equivalent built-in Presto function if one exists.
- If the function must be used in the join predicate, check whether the connector offers a native (non-external) variant or pushdown of the predicate.
- As a last resort, restructure as a lateral/cross join plus WHERE if the planner supports it for that function.
Example fix
// before SELECT * FROM a JOIN b ON a.id = b.id AND my_ext_func(a.ts) > 10 // after WITH pre AS (SELECT *, my_ext_func(ts) AS f FROM a) SELECT * FROM pre JOIN b ON pre.id = b.id AND pre.f > 10
Defensive patterns
Strategy: validation
Validate before calling
// Inspect join filters for external functions before submitting
boolean hasExternalCallInJoin(PlanNode joinNode, Metadata metadata) {
return joinNode.getFilter()
.map(f -> f.accept(new ExternalCallExpressionChecker(metadata), null))
.orElse(false);
}
if (hasExternalCallInJoin(node, metadata)) { /* rewrite query to precompute */ } Prevention
- Keep external/connector functions out of JOIN ON clauses; precompute them in subqueries
- Prefer built-in scalar functions in join predicates
- Wrap external function calls in CTEs before joining
- Add query linting in your SQL-generation layer to flag external functions inside join conditions
When it happens
Trigger: Running a query with an explicit join condition (e.g. `... JOIN t ON a.x = b.y AND ext_func(a.z)`) where `ext_func` is registered by a connector as an external function, so the filter expression passed the externalCallExpressionChecker during plan sanity validation in visitJoin.
Common situations: Queries mixing built-in and connector-provided UDFs in JOIN ON clauses; migrations after a connector adds external function support; users assuming external functions behave like ordinary scalar functions everywhere.
Understand the failure class
Background: Presto NOT_SUPPORTED error: what "not supported" means and how to fix it — this error's family across 3 libraries.
Related errors
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/24bd190cf51ae63f.
Report an issue: GitHub.