prestodb/presto · error · SemanticException
PROCEDURE_NOT_FOUND
PROCEDURE_NOT_FOUND
Error message
Distributed procedure not registered:
What it means
CALL statements can target either connector-local procedures or distributed procedures. When the statement resolves to a distributed procedure invocation, the analyzer asks the ProcedureRegistry whether the connector has registered that procedure; if not, it raises PROCEDURE_NOT_FOUND with the procedure name appended to a static message string.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java:1536
protected Scope visitCall(Call call, Optional<Scope> scope)
{
if (analysis.isDescribe()) {
return createAndAssignScope(call, scope);
}
Optional<QualifiedObjectName> procedureNameOptional = analysis.getProcedureName();
QualifiedObjectName procedureName;
if (!procedureNameOptional.isPresent()) {
procedureName = createQualifiedObjectName(session, call, call.getName(), metadata);
analysis.setProcedureName(Optional.of(procedureName));
}
else {
procedureName = procedureNameOptional.get();
}
ConnectorId connectorId = metadata.getCatalogHandle(session, procedureName.getCatalogName())
.orElseThrow(() -> new SemanticException(MISSING_CATALOG, call, "Catalog %s does not exist", procedureName.getCatalogName()));
if (!metadata.getProcedureRegistry().isDistributedProcedure(connectorId, toSchemaTableName(procedureName))) {
throw new SemanticException(PROCEDURE_NOT_FOUND, "Distributed procedure not registered: " + procedureName);
}
DistributedProcedure procedure = metadata.getProcedureRegistry().resolveDistributed(connectorId, toSchemaTableName(procedureName));
Object[] values = extractParameterValuesInOrder(call, procedure, metadata, session, analysis.getParameters());
accessControl.checkCanCallProcedure(session.getRequiredTransactionId(), session.getIdentity(), session.getAccessControlContext(), procedureName);
analysis.setUpdateInfo(call.getUpdateInfo());
DistributedProcedure.DistributedProcedureType procedureType = procedure.getType();
switch (procedureType) {
case TABLE_DATA_REWRITE:
TableDataRewriteDistributedProcedure tableDataRewriteDistributedProcedure = (TableDataRewriteDistributedProcedure) procedure;
QualifiedName qualifiedName = QualifiedName.of(tableDataRewriteDistributedProcedure.getSchema(values), tableDataRewriteDistributedProcedure.getTableName(values));
QualifiedObjectName tableName = createQualifiedObjectName(session, call, qualifiedName, metadata);
analysis.addAccessControlCheckForTable(
TABLE_INSERT,
new AccessControlInfoForTable(
accessControl,
session.getIdentity(),View on GitHub (pinned to 55bb57d202)
Solutions
- Verify the exact procedure name and signature in the connector's documentation and correct the CALL statement.
- Upgrade the connector/coordinator to a version that registers the distributed procedure.
- Point the CALL at the correct catalog whose connector supports the procedure.
- If the connector offers the operation only as a local procedure, use its supported invocation form instead of the distributed one.
Example fix
// before (connector lacks distributed expire_procedure)
CALL iceberg.system.expire_snapshots('db.t', TIMESTAMP '2026-01-01');
// after: upgrade connector, or use supported local form
CALL iceberg.system.expire_snapshots(schema_name => 'db', table_name => 't', older_than => TIMESTAMP '2026-01-01'); Defensive patterns
Strategy: try-catch
Validate before calling
boolean registered = (boolean) query("SELECT count(*) > 0 FROM system.metadata.procedures " +
"WHERE procedure_name = ? AND catalog_name = ?", procName, catalog).get(0).get(0);
if (!registered) throw new IllegalArgumentException("Procedure not available on catalog " + catalog); Try / catch
try {
execute(callSql);
} catch (SemanticException e) {
if (e.getCode() == PROCEDURE_NOT_FOUND) {
log.error("Distributed procedure {} not registered on this connector; check version/catalog", callSql);
throw new UnsupportedOperationException("Procedure unavailable in deployed connector", e);
}
throw e;
} Prevention
- List available procedures via system.metadata.procedures for the target catalog before calling.
- Pin coordinator/connector versions so procedure availability matches the SQL you ship.
- Spell procedure names from documentation verbatim, including catalog qualifier.
When it happens
Trigger: Executing a CALL <catalog>.<schema>.<proc>(...) that is treated as a distributed procedure while metadata.getProcedureRegistry().isDistributedProcedure(connectorId, name) returns false — i.e., the connector never registered that procedure as distributed (or registered it only as a local procedure, or not at all).
Common situations: Calling a system-style procedure (e.g., sync/expire/compaction) against a connector version that does not implement it; misspelled procedure name; procedure exists in a newer connector release than the deployed coordinator; calling on the wrong catalog whose connector lacks the procedure.
Related errors
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/9500c7a0059f6e4e.
Report an issue: GitHub.