apache/druid · warning · BasicSecurityDBResourceException
User [%s] already has role [%s].
Error message
User [%s] already has role [%s].
What it means
BasicSecurityDBResourceException thrown when assigning a role the user already holds. The coordinator checks the user's current role set before mutating it, rejecting redundant assignments so the role list stays a clean set. Purely informational in effect: the desired end state already exists.
Source
Thrown at extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java:1000
objectMapper,
oldRoleMapValue
);
if (roleMap.get(roleName) == null) {
throw new BasicSecurityDBResourceException("Role [%s] does not exist.", roleName);
}
byte[] oldUserMapValue = getCurrentUserMapBytes(prefix);
Map<String, BasicAuthorizerUser> userMap = BasicAuthUtils.deserializeAuthorizerUserMap(
objectMapper,
oldUserMapValue
);
BasicAuthorizerUser user = userMap.get(userName);
if (userMap.get(userName) == null) {
throw new BasicSecurityDBResourceException("User [%s] does not exist.", userName);
}
if (user.getRoles().contains(roleName)) {
throw new BasicSecurityDBResourceException("User [%s] already has role [%s].", userName, roleName);
}
user.getRoles().add(roleName);
byte[] newUserMapValue = BasicAuthUtils.serializeAuthorizerUserMap(objectMapper, userMap);
// Role map is unchanged, but submit as an update to ensure that the table didn't change (e.g., role deleted)
return tryUpdateUserAndRoleMap(
prefix,
userMap, oldUserMapValue, newUserMapValue,
roleMap, oldRoleMapValue, oldRoleMapValue
);
}
private boolean unassignUserRoleOnce(String prefix, String userName, String roleName)
{
byte[] oldRoleMapValue = getCurrentRoleMapBytes(prefix);
Map<String, BasicAuthorizerRole> roleMap = BasicAuthUtils.deserializeAuthorizerRoleMap(
objectMapper,View on GitHub (pinned to 9b90983fd2)
Solutions
- Check the user's current roles via GET .../users/<userName> and skip assignment if present
- Treat 'already has role' responses as success in idempotent automation
- Track assignment state in your provisioning tool to avoid re-issuing assignments
- Remove the role first (if a reset is intended) before re-assigning
Example fix
// before
client.assignRole("default", "alice", "readRole"); // fails on rerun
// after
if (!userRoles("default", "alice").contains("readRole")) {
client.assignRole("default", "alice", "readRole");
} Defensive patterns
Strategy: validation
Validate before calling
java.util.Set<String> roles = getUser("default", userName).getRoles();
if (roles.contains(roleName)) skipAssignment(userName, roleName); Try / catch
try {
client.assignRole("default", userName, roleName);
} catch (BasicSecurityDBResourceException e) {
if (e.getMessage().contains("already has role")) {
log.info("{} already has {}", userName, roleName); // treat as success
} else { throw e; }
} Prevention
- Fetch current roles before assignment and skip if present
- Treat 'already has role' as success in idempotent automation
- Avoid re-issuing assignments after successful runs
When it happens
Trigger: POST .../users/<userName>/roles with roleName already present in the user's roles (e.g. from a previous run of the same script).
Common situations: Non-idempotent provisioning scripts run repeatedly; concurrent admins assigning the same role; retry logic that re-sends an assignment that already succeeded; bulk imports that assume empty role sets.
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
- User [%s] already exists.
- Group mapping [%s] already exists.
- Role [%s] already exists.
- Group mapping [%s] does not exist.
- Role [%s] does not exist.
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/2be51e66547cea6f.
Report an issue: GitHub.