apache/druid · error · BasicSecurityDBResourceException

Role [%s] already exists.

Error message

Role [%s] already exists.

What it means

BasicSecurityDBResourceException thrown by createRoleOnce when a role with the requested name already exists in the authorizer's role map. Role names are unique within an authorizer, and the coordinator refuses to create duplicates so existing permissions are never silently overwritten. It is a client-input/validation error surfaced via the coordinator API.

Source

Thrown at extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java:924

  private boolean createGroupMappingOnce(String prefix, BasicAuthorizerGroupMapping groupMapping)
  {
    byte[] oldValue = getCurrentGroupMappingMapBytes(prefix);
    Map<String, BasicAuthorizerGroupMapping> groupMappingMap = BasicAuthUtils.deserializeAuthorizerGroupMappingMap(objectMapper, oldValue);
    if (groupMappingMap.get(groupMapping.getName()) != null) {
      throw new BasicSecurityDBResourceException("Group mapping [%s] already exists.", groupMapping.getName());
    } else {
      groupMappingMap.put(groupMapping.getName(), groupMapping);
    }
    byte[] newValue = BasicAuthUtils.serializeAuthorizerGroupMappingMap(objectMapper, groupMappingMap);
    return tryUpdateGroupMappingMap(prefix, groupMappingMap, oldValue, newValue);
  }

  private boolean createRoleOnce(String prefix, String roleName)
  {
    byte[] oldValue = getCurrentRoleMapBytes(prefix);
    Map<String, BasicAuthorizerRole> roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(objectMapper, oldValue);
    if (roleMap.get(roleName) != null) {
      throw new BasicSecurityDBResourceException("Role [%s] already exists.", roleName);
    } else {
      roleMap.put(roleName, new BasicAuthorizerRole(roleName, null));
    }
    byte[] newValue = BasicAuthUtils.serializeAuthorizerRoleMap(objectMapper, roleMap);
    return tryUpdateRoleMap(prefix, roleMap, oldValue, newValue);
  }

  private boolean deleteRoleOnce(String prefix, String roleName)
  {
    byte[] oldRoleMapValue = getCurrentRoleMapBytes(prefix);
    Map<String, BasicAuthorizerRole> roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(
        objectMapper,
        oldRoleMapValue
    );
    if (roleMap.get(roleName) == null) {
      throw new BasicSecurityDBResourceException("Role [%s] does not exist.", roleName);
    } else {
      roleMap.remove(roleName);

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Check role existence via GET /druid-ext/basic-security/authorization/db/<authorizer>/roles/<roleName> first
  2. Use the update API to modify permissions of an existing role instead of create
  3. Skip creation when the role already exists in automation scripts
  4. Delete the existing role (checking user assignments first) if a fresh role is required

Example fix

// before
createRole("default", "readRole");
// after
if (!roleExists("default", "readRole")) {
  createRole("default", "readRole");
}
Defensive patterns

Strategy: validation

Validate before calling

boolean exists = getRoles("default").stream()
    .anyMatch(r -> r.equals(roleName));
if (exists) skipCreation(roleName);

Try / catch

try {
  client.createRole("default", roleName);
} catch (BasicSecurityDBResourceException e) {
  if (e.getMessage().contains("already exists")) {
    log.info("Role {} already exists", roleName);
  } else { throw e; }
}

Prevention

When it happens

Trigger: POST /druid-ext/basic-security/authorization/db/<authorizer>/roles/<roleName> for a role that already exists.

Common situations: Idempotency issues in provisioning scripts that define roles; re-importing a role export that already exists; multiple admins creating the same role concurrently; calling create when the intent was to update the role's permissions.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/2be0d4980b3583b4. Report an issue: GitHub.