prestodb/presto · error · SemanticException

TABLE_ALREADY_EXISTS

TABLE_ALREADY_EXISTS

Error message

Destination table '%s' already exists

What it means

CREATE TABLE AS SELECT (CTAS) refuses to overwrite an existing table unless the statement uses CREATE TABLE IF NOT EXISTS (or the session's existing-table behavior). When metadataResolver reports the destination table already exists and no NOT EXISTS clause short-circuits, visitCreateTableAsSelect throws TABLE_ALREADY_EXISTS.

Source

Thrown at presto-main-base/src/main/java/com/facebook/presto/sql/analyzer/StatementAnalyzer.java:819

            analysis.setUpdateInfo(node.getUpdateInfo());

            // turn this into a query that has a new table writer node on top.
            QualifiedObjectName targetTable = createQualifiedObjectName(session, node, node.getName(), metadata);
            analysis.setCreateTableDestination(targetTable);

            if (metadataResolver.tableExists(targetTable)) {
                if (node.isNotExists()) {
                    analysis.setCreateTableAsSelectNoOp(true);
                    warningCollector.add(new PrestoWarning(
                            StandardWarningCode.SEMANTIC_WARNING,
                            format("Table '%s' already exists, skipping table creation", targetTable)));
                    // Analyze the inner query to populate view definitions for access control checks
                    if (isAlwaysAnalyzeCreateTableQueryEnabled(session)) {
                        process(node.getQuery(), scope);
                    }
                    return createAndAssignScope(node, scope, Field.newUnqualified(node.getLocation(), "rows", BIGINT));
                }
                throw new SemanticException(TABLE_ALREADY_EXISTS, node, "Destination table '%s' already exists", targetTable);
            }

            validateProperties(node.getProperties(), scope);
            analysis.setCreateTableProperties(mapFromProperties(node.getProperties()));

            node.getColumnAliases().ifPresent(analysis::setCreateTableColumnAliases);
            analysis.setCreateTableComment(node.getComment());

            analysis.addAccessControlCheckForTable(TABLE_CREATE, new AccessControlInfoForTable(accessControl, session.getIdentity(), session.getTransactionId(), session.getAccessControlContext(), targetTable));

            analysis.setCreateTableAsSelectWithData(node.isWithData());

            // analyze the query that creates the table
            Scope queryScope = process(node.getQuery(), scope);

            ImmutableList.Builder<OutputColumnMetadata> outputColumns = ImmutableList.builder();

            if (node.getColumnAliases().isPresent()) {

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Use CREATE TABLE IF NOT EXISTS to skip when the destination exists
  2. DROP TABLE the destination first if overwrite is intended
  3. DROP the old table and rerun the CTAS to recreate it

Example fix

-- before
CREATE TABLE reports.daily AS SELECT * FROM events;
-- after
CREATE TABLE IF NOT EXISTS reports.daily AS SELECT * FROM events;
Defensive patterns

Strategy: try-catch

Validate before calling

// before CTAS:
// boolean exists = metadataResolver.tableExists(qualifiedTableName);

Try / catch

catch (SemanticException e) { if (e.getCode() == TABLE_ALREADY_EXISTS) { /* drop or use IF NOT EXISTS */ } }

Prevention

When it happens

Trigger: CREATE TABLE AS SELECT against a destination qualified name that already exists in the connector, without IF NOT EXISTS and without ignore-existing session behavior.

Common situations: Re-running an idempotent-by-intent pipeline script; a previous partially successful run left the table behind; name collision between environments pointing at the same catalog.

Related errors


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