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

  1. Check existence first: query metadata.listRoles (or SHOW ROLES in the catalog) before issuing CREATE ROLE.
  2. Use a different role name, since role names are lowercased and must be unique per catalog.
  3. Wrap creation in your script's error handling to treat ROLE_ALREADY_EXIST as a no-op for idempotent runs.
  4. 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

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.