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

  1. Verify the exact procedure name and signature in the connector's documentation and correct the CALL statement.
  2. Upgrade the connector/coordinator to a version that registers the distributed procedure.
  3. Point the CALL at the correct catalog whose connector supports the procedure.
  4. 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

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.