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
- Use CREATE TABLE IF NOT EXISTS to skip when the destination exists
- DROP TABLE the destination first if overwrite is intended
- 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
- Prefer CREATE TABLE IF NOT EXISTS for idempotent pipelines
- Add drop-and-recreate steps to job setup when overwrite is intended
- Check destination existence in the orchestrator before submitting CTAS
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.