prestodb/presto · error · SemanticException
TABLE_ALREADY_EXISTS
TABLE_ALREADY_EXISTS
Error message
Table '%s' already exists
What it means
CreateTableTask.internalExecute throws SemanticException(TABLE_ALREADY_EXISTS) when a table handle is already resolvable for the target qualified name and the statement did not include IF NOT EXISTS. The check happens against the metadata resolver before any connector-side table creation, so the error is raised before side effects.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/execution/CreateTableTask.java:112
}
@Override
public ListenableFuture<?> execute(CreateTable statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, Session session, List<Expression> parameters, WarningCollector warningCollector, String query)
{
return internalExecute(statement, metadata, accessControl, session, parameters, warningCollector, query);
}
@VisibleForTesting
public ListenableFuture<?> internalExecute(CreateTable statement, Metadata metadata, AccessControl accessControl, Session session, List<Expression> parameters, WarningCollector warningCollector, String query)
{
checkArgument(!statement.getElements().isEmpty(), "no columns for table");
Map<NodeRef<Parameter>, Expression> parameterLookup = parameterExtractor(statement, parameters);
QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getName(), metadata);
Optional<TableHandle> tableHandle = metadata.getMetadataResolver(session).getTableHandle(tableName);
if (tableHandle.isPresent()) {
if (!statement.isNotExists()) {
throw new SemanticException(TABLE_ALREADY_EXISTS, statement, "Table '%s' already exists", tableName);
}
return immediateFuture(null);
}
ConnectorId connectorId = getConnectorIdOrThrow(session, metadata, tableName.getCatalogName());
LinkedHashMap<String, ColumnMetadata> columns = new LinkedHashMap<>();
Map<String, Object> inheritedProperties = ImmutableMap.of();
boolean includingProperties = false;
List<TableConstraint<String>> constraints = new ArrayList<>();
for (TableElement element : statement.getElements()) {
if (element instanceof ColumnDefinition) {
ColumnDefinition column = (ColumnDefinition) element;
String columnName = column.getName().getValue();
String name = metadata.normalizeIdentifier(session, tableName.getCatalogName(), columnName);
Type type;
try {
type = metadata.getType(parseTypeSignature(column.getType()));View on GitHub (pinned to 55bb57d202)
Solutions
- Use CREATE TABLE IF NOT EXISTS to tolerate pre-existing tables.
- Check existence first (SHOW TABLES / metadata.getTableHandle) before issuing DDL.
- Drop the existing table explicitly if it is stale: DROP TABLE before re-creating.
- Have retry scripts treat TABLE_ALREADY_EXISTS as a no-op success.
Example fix
-- before CREATE TABLE hive.default.events (id BIGINT); -- fails on rerun -- after CREATE TABLE IF NOT EXISTS hive.default.events (id BIGINT);
Defensive patterns
Strategy: try-catch
Validate before calling
QualifiedObjectName tableName = createQualifiedObjectName(session, statement, statement.getName(), metadata);
if (metadata.getMetadataResolver(session).getTableHandle(tableName).isPresent() && !statement.isNotExists()) {
// skip creation or add IF NOT EXISTS
return;
} Type guard
boolean tableMissing(Session session, Metadata metadata, QualifiedObjectName tableName) {
return metadata.getMetadataResolver(session).getTableHandle(tableName).isEmpty();
} Try / catch
try {
createTable(session, statement, parameters);
} catch (SemanticException e) {
if (e.getCode() == TABLE_ALREADY_EXISTS) {
// idempotent: verify schema matches, then continue
} else {
throw e;
}
} Prevention
- Use CREATE TABLE IF NOT EXISTS for rerunnable pipelines
- Check getTableHandle before issuing CREATE TABLE in scripts
- Clean up partially created tables on retried jobs, or make DDL idempotent
- Prefer CREATE OR REPLACE semantics where the connector supports it
When it happens
Trigger: CREATE TABLE cat.schema.tbl ... where getTableHandle returns a handle and isNotExists() is false; re-running DDL scripts; concurrent pipelines racing to create the same table.
Common situations: Non-idempotent ETL bootstrap SQL; jobs retried after partial failure where the first attempt created the table; name collisions after identifier normalization.
Related errors
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/6800ec2d11da59ed.
Report an issue: GitHub.