apache/druid · error · BasicSecurityDBResourceException
Group mapping [%s] already has role [%s].
Error message
Group mapping [%s] already has role [%s].
What it means
Thrown when assigning a role to a group mapping that already contains that role. Druid treats the assignment as already satisfied and throws BasicSecurityDBResourceException instead of allowing a duplicate entry in the mapping's role set.
Source
Thrown at extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java:1072
objectMapper,
oldRoleMapValue
);
if (roleMap.get(roleName) == null) {
throw new BasicSecurityDBResourceException("Role [%s] does not exist.", roleName);
}
byte[] oldGroupMappingMapValue = getCurrentGroupMappingMapBytes(prefix);
Map<String, BasicAuthorizerGroupMapping> groupMappingMap = BasicAuthUtils.deserializeAuthorizerGroupMappingMap(
objectMapper,
oldGroupMappingMapValue
);
BasicAuthorizerGroupMapping groupMapping = groupMappingMap.get(groupMappingName);
if (groupMappingMap.get(groupMappingName) == null) {
throw new BasicSecurityDBResourceException("Group mapping [%s] does not exist.", groupMappingName);
}
if (groupMapping.getRoles().contains(roleName)) {
throw new BasicSecurityDBResourceException("Group mapping [%s] already has role [%s].", groupMappingName, roleName);
}
groupMapping.getRoles().add(roleName);
byte[] newGroupMapValue = BasicAuthUtils.serializeAuthorizerGroupMappingMap(objectMapper, groupMappingMap);
// Role map is unchanged, but submit as an update to ensure that the table didn't change (e.g., role deleted)
return tryUpdateGroupMappingAndRoleMap(
prefix,
groupMappingMap, oldGroupMappingMapValue, newGroupMapValue,
roleMap, oldRoleMapValue, oldRoleMapValue
);
}
private boolean unassignGroupMappingRoleOnce(String prefix, String groupMappingName, String roleName)
{
byte[] oldRoleMapValue = getCurrentRoleMapBytes(prefix);
Map<String, BasicAuthorizerRole> roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(
objectMapper,View on GitHub (pinned to 9b90983fd2)
Solutions
- Check the mapping's current roles via GET .../groupMappings/<name> and skip the assignment if the role is present
- Treat the 400 'already has role' response as success in idempotent provisioning flows
- Deduplicate the role list in automation config before applying
- Add locking/leader election if multiple jobs assign roles concurrently
Example fix
// before
client.post("/druid-ext/basic-security/authorization/db/v1/internal-auth/groupMappings/ldap-admins/roles/admin");
// after (idempotent)
GroupMapping gm = client.getGroupMapping("internal-auth", "ldap-admins");
if (!gm.getRoles().contains("admin")) {
client.post("/druid-ext/basic-security/authorization/db/v1/internal-auth/groupMappings/ldap-admins/roles/admin");
} Defensive patterns
Strategy: try-catch
Validate before calling
// skip assignment if already present GroupMapping gm = getGroupMapping(authorizer, mappingName); if (gm != null && gm.getRoles().contains(roleName)) return;
Type guard
boolean hasRole(GroupMapping m, String role) { return m != null && m.getRoles() != null && m.getRoles().contains(role); } Try / catch
try {
assignRoleToGroupMapping(authorizer, mappingName, roleName);
} catch (BasicSecurityDBResourceException e) {
if (e.getMessage().contains("already has role")) { /* idempotent success */ }
else throw e;
} Prevention
- Design provisioning as idempotent: check-then-apply or tolerate 'already has role'
- Deduplicate role lists in config before applying
- Serialize concurrent role-assignment jobs for the same mapping
- Fetch current mapping state instead of relying on cached listings
When it happens
Trigger: POST to /groupMappings/<name>/roles/<role> when the mapping's roles list already includes the role; re-running a provisioning script without idempotency checks; two admins or jobs performing the same assignment concurrently.
Common situations: Idempotent infrastructure-as-code re-apply hitting an already-applied state; retry logic resending a request that actually succeeded the first time; duplicate entries in automation config applied in a loop.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- User [%s] does not have role [%s].
- Group mapping [%s] does not have role [%s].
- User [%s] does not exist.
- User [%s] does not exist.
- Could not create user [%s] due to concurrent update contenti
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/6fcce67ba13f5a6b.
Report an issue: GitHub.