apache/druid · error · BasicSecurityDBResourceException
Group mapping [%s] does not have role [%s].
Error message
Group mapping [%s] does not have role [%s].
What it means
Thrown when unassigning a role from a group mapping that does not currently hold that role. Druid validates membership before mutating the mapping and throws BasicSecurityDBResourceException rather than performing a no-op removal, since the subsequent metadata-store compare-and-swap would otherwise mask concurrent changes.
Source
Thrown at extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java:1108
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] does not have role [%s].", groupMappingName, roleName);
}
groupMapping.getRoles().remove(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 setPermissionsOnce(String prefix, String roleName, List<ResourceAction> permissions)
{
byte[] oldRoleMapValue = getCurrentRoleMapBytes(prefix);
Map<String, BasicAuthorizerRole> roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(
objectMapper,View on GitHub (pinned to 9b90983fd2)
Solutions
- Fetch the mapping (GET .../groupMappings/<name>) and only delete roles it actually contains
- Treat this 400 as success in 'ensure role absent' idempotent flows
- Refresh cached group-mapping state before retrying
- Serialize role-management operations if multiple admins/tools mutate the same mapping
Example fix
// before (blind revoke)
client.delete("/druid-ext/basic-security/authorization/db/v1/internal-auth/groupMappings/ldap-admins/roles/auditor");
// after
GroupMapping gm = client.getGroupMapping("internal-auth", "ldap-admins");
if (gm.getRoles().contains("auditor")) {
client.delete("/druid-ext/basic-security/authorization/db/v1/internal-auth/groupMappings/ldap-admins/roles/auditor");
} Defensive patterns
Strategy: try-catch
Validate before calling
// only revoke roles the mapping actually has 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 {
unassignRoleFromGroupMapping(authorizer, mappingName, roleName);
} catch (BasicSecurityDBResourceException e) {
if (e.getMessage().contains("does not have role")) { /* already revoked: success */ }
else throw e;
} Prevention
- Fetch the mapping's current roles before revoking
- Make revoke flows idempotent (ignore 'does not have role')
- Refresh client state after other admins' changes
- Use one management path (console or API) at a time for the same mapping
When it happens
Trigger: DELETE to /groupMappings/<name>/roles/<role> when the mapping never had the role; another admin already removed the assignment; stale client state from an outdated group-mapping listing; re-running cleanup scripts.
Common situations: Idempotent revoke automation hitting an already-revoked state; concurrent admin edits via the Druid console and API; config reconciliation jobs assuming role assignments that were never made.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- User [%s] does not have role [%s].
- Group mapping [%s] already has 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/dd05ff9f6c7a5fb8.
Report an issue: GitHub.