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

  1. Check existence first with GET /druid-ext/basic-security/authorization/db/<authorizer>/users/<userName> before creating
  2. Make provisioning scripts idempotent: treat 'already exists' as success or use the update APIs instead of create
  3. Verify you are targeting the intended authorizer prefix (e.g. default vs another authorizer name)
  4. 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

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


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/b81f366d6b6e8c80. Report an issue: GitHub.