apache/druid · error · BasicSecurityDBResourceException
User [%s] already exists.
Error message
User [%s] already exists.
What it means
BasicSecurityDBResourceException thrown by createUserOnce when a user with the requested name is already present in the coordinator's authorizer user map for the given authenticator/authorizer prefix. The coordinator does a read-check-update against the metadata store and refuses to create duplicate users because user names must be unique within an authorizer. Callers see it surfaced over HTTP as a 400-class response from the coordinator basic-security API.
Source
Thrown at extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java:885
private boolean deleteUserOnce(String prefix, String userName)
{
byte[] oldValue = getCurrentUserMapBytes(prefix);
Map<String, BasicAuthorizerUser> userMap = BasicAuthUtils.deserializeAuthorizerUserMap(objectMapper, oldValue);
if (userMap.get(userName) == null) {
throw new BasicSecurityDBResourceException("User [%s] does not exist.", userName);
} else {
userMap.remove(userName);
}
byte[] newValue = BasicAuthUtils.serializeAuthorizerUserMap(objectMapper, userMap);
return tryUpdateUserMap(prefix, userMap, oldValue, newValue);
}
private boolean createUserOnce(String prefix, String userName)
{
byte[] oldValue = getCurrentUserMapBytes(prefix);
Map<String, BasicAuthorizerUser> userMap = BasicAuthUtils.deserializeAuthorizerUserMap(objectMapper, oldValue);
if (userMap.get(userName) != null) {
throw new BasicSecurityDBResourceException("User [%s] already exists.", userName);
} else {
userMap.put(userName, new BasicAuthorizerUser(userName, null));
}
byte[] newValue = BasicAuthUtils.serializeAuthorizerUserMap(objectMapper, userMap);
return tryUpdateUserMap(prefix, userMap, oldValue, newValue);
}
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);View on GitHub (pinned to 9b90983fd2)
Solutions
- Check existence first with GET /druid-ext/basic-security/authorization/db/<authorizer>/users/<userName> before creating
- Make provisioning scripts idempotent: treat 'already exists' as success or use the update APIs instead of create
- Verify you are targeting the intended authorizer prefix (e.g. default vs another authorizer name)
- If the existing user is wrong, delete it via DELETE .../users/<userName> then recreate
Example fix
// before: blind create on every run
client.createUser("authentication", "default", "alice");
// after: idempotent create
if (!userExists("default", "alice")) {
client.createUser("authentication", "default", "alice");
} Defensive patterns
Strategy: validation
Validate before calling
boolean exists = client.getUser("default", userName) != null;
if (exists) throw new IllegalStateException("User already created: " + userName); Try / catch
try {
client.createUser("default", userName);
} catch (BasicSecurityDBResourceException e) {
if (e.getMessage().contains("already exists")) {
log.info("User {} already exists, skipping", userName);
} else { throw e; }
} Prevention
- Check existence with the GET endpoint before creating
- Make all provisioning scripts idempotent
- Use consistent, case-exact user names across environments
- Verify the authorizer name in the URL before API calls
When it happens
Trigger: Calling POST /druid-ext/basic-security/authorization/db/<authorizer>/users/<userName> (or BasicAuthorizerResourceStorageUpdater client createUser) for a userName that already exists in that authorizer's user map.
Common situations: Automated user-provisioning scripts run twice without idempotency; re-running an init/bootstrap script that creates users; case/whitespace mismatches lead operators to think a user is new when it exists; migrating configs between clusters where the user already exists.
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
- Group mapping [%s] already exists.
- Role [%s] already exists.
- User [%s] already has role [%s].
- 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/b81f366d6b6e8c80.
Report an issue: GitHub.