apache/druid · error · BasicSecurityDBResourceException

Group mapping [%s] already exists.

Error message

Group mapping [%s] already exists.

What it means

BasicSecurityDBResourceException thrown by createGroupMappingOnce when a group mapping with the same name already exists in the authorizer's group-mapping map. Group mapping names are unique keys, so the coordinator rejects duplicate creation rather than overwriting existing permissions. Prevents silently replacing an LDAP group-to-role mapping.

Source

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

  private boolean deleteGroupMappingOnce(String prefix, String groupMappingName)
  {
    byte[] oldValue = getCurrentGroupMappingMapBytes(prefix);
    Map<String, BasicAuthorizerGroupMapping> groupMappingMap = BasicAuthUtils.deserializeAuthorizerGroupMappingMap(objectMapper, oldValue);
    if (groupMappingMap.get(groupMappingName) == null) {
      throw new BasicSecurityDBResourceException("Group mapping [%s] does not exist.", groupMappingName);
    } else {
      groupMappingMap.remove(groupMappingName);
    }
    byte[] newValue = BasicAuthUtils.serializeAuthorizerGroupMappingMap(objectMapper, groupMappingMap);
    return tryUpdateGroupMappingMap(prefix, groupMappingMap, oldValue, newValue);
  }

  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);

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Check GET /druid-ext/basic-security/authorization/db/<authorizer>/groupMappings/<name> before creating
  2. To change an existing mapping, use the PUT/update endpoint rather than create
  3. Make setup scripts idempotent by skipping existing mappings
  4. Delete the existing mapping first if a clean re-create is truly intended

Example fix

// before
createGroupMapping("default", mapping); // fails if exists
// after
if (groupMappingExists("default", mapping.getName())) {
  updateGroupMapping("default", mapping.getName(), mapping);
} else {
  createGroupMapping("default", mapping);
}
Defensive patterns

Strategy: validation

Validate before calling

boolean exists = getGroupMappings("default").stream()
    .anyMatch(gm -> gm.equals(mapping.getName()));
if (exists) { update instead of create }

Try / catch

try {
  client.createGroupMapping("default", mapping);
} catch (BasicSecurityDBResourceException e) {
  if (e.getMessage().contains("already exists")) {
    client.updateGroupMapping("default", mapping.getName(), mapping);
  } else { throw e; }
}

Prevention

When it happens

Trigger: POST /druid-ext/basic-security/authorization/db/<authorizer>/groupMappings/<groupMappingName> with a name that already exists in that authorizer.

Common situations: Re-running a setup script that defines LDAP group mappings; copying mappings between clusters where the name already exists; wanting to change a mapping but calling create instead of update.

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/2272d2dbef914de2. Report an issue: GitHub.