prestodb/presto · error · SemanticException
ROLE_ALREADY_EXIST
ROLE_ALREADY_EXIST
Error message
Role '%s' already exists
What it means
CreateRoleTask.execute throws SemanticException(ROLE_ALREADY_EXIST) after checkCanCreateRole when metadata.listRoles shows the requested role name already exists in the target catalog. Role names are case-insensitive (lowercased before comparison), so 'Admin' and 'admin' collide. Presto refuses to create a duplicate rather than silently reusing it.
Source
Thrown at presto-main-base/src/main/java/com/facebook/presto/execution/CreateRoleTask.java:56
public class CreateRoleTask
implements DDLDefinitionTask<CreateRole>
{
@Override
public String getName()
{
return "CREATE ROLE";
}
@Override
public ListenableFuture<?> execute(CreateRole statement, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, Session session, List<Expression> parameters, WarningCollector warningCollector, String query)
{
String catalog = createCatalogName(session, statement);
String role = statement.getName().getValueLowerCase();
Optional<PrestoPrincipal> grantor = statement.getGrantor().map(specification -> createPrincipal(session, specification));
accessControl.checkCanCreateRole(session.getRequiredTransactionId(), session.getIdentity(), session.getAccessControlContext(), role, grantor, catalog);
Set<String> existingRoles = metadata.listRoles(session, catalog);
if (existingRoles.contains(role)) {
throw new SemanticException(ROLE_ALREADY_EXIST, statement, "Role '%s' already exists", role);
}
if (grantor.isPresent() && grantor.get().getType() == ROLE && !existingRoles.contains(grantor.get().getName())) {
throw new SemanticException(MISSING_ROLE, statement, "Role '%s' does not exist", grantor.get().getName());
}
metadata.createRole(session, role, grantor, catalog);
return immediateFuture(null);
}
}
View on GitHub (pinned to 55bb57d202)
Solutions
- Check existence first: query metadata.listRoles (or SHOW ROLES in the catalog) before issuing CREATE ROLE.
- Use a different role name, since role names are lowercased and must be unique per catalog.
- Wrap creation in your script's error handling to treat ROLE_ALREADY_EXIST as a no-op for idempotent runs.
- If the role is stale/unwanted, drop it (DROP ROLE) and recreate with the desired definition.
Example fix
-- before CREATE ROLE admin; -- fails if ADMIN already exists -- after -- only create if missing (script logic) IF 'admin' NOT IN (SELECT role_name FROM information_schema.roles WHERE catalog='hive') THEN CREATE ROLE admin; END IF;
Defensive patterns
Strategy: try-catch
Validate before calling
Set<String> existing = metadata.listRoles(session, catalog);
String role = statement.getName().getValueLowerCase();
if (existing.contains(role)) {
// skip creation or choose another name
return;
} Type guard
boolean roleExists(String roleName, String catalog, Metadata metadata, Session session) {
return metadata.listRoles(session, catalog).contains(roleName.getValueLowerCase());
} Try / catch
try {
createRole(session, statement);
} catch (SemanticException e) {
if (e.getCode() == ROLE_ALREADY_EXIST) {
// idempotent: treat as success
} else {
throw e;
}
} Prevention
- Always lowercase role names when checking existence, matching getValueLowerCase
- Make role-provisioning scripts idempotent by catching ROLE_ALREADY_EXIST
- Query SHOW ROLES / information_schema.roles before CREATE ROLE
- Keep role names unique per catalog and avoid case-only differences
When it happens
Trigger: CREATE ROLE <name> where the lowercased name already exists in the connector's role set for the statement's catalog.
Common situations: Idempotent provisioning scripts that re-run CREATE ROLE without IF NOT EXISTS semantics; case differences hiding an existing role; roles created in another catalog where listRoles for this catalog still reports it.
Related errors
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/98aec28dae1f6cd6.
Report an issue: GitHub.