apache/druid · error · BasicSecurityDBResourceException

User [%s] does not exist.

Error message

User [%s] does not exist.

What it means

deleteUser checks the authorizer user map before removal; if the named user is absent from the map deserialized from metadata storage, it immediately throws BasicSecurityDBResourceException. This is a straightforward pre-condition failure: the delete target does not exist in the given authorizer's user map. Unlike the contention errors, no retries occur — the check happens on the first read.

Source

Thrown at extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java:872

      } else {
        attempts++;
      }
      try {
        Thread.sleep(ThreadLocalRandom.current().nextLong(UPDATE_RETRY_DELAY));
      }
      catch (InterruptedException ie) {
        throw new RuntimeException(ie);
      }
    }
    throw new ISE("Could not set permissions for role [%s] due to concurrent update contention.", roleName);
  }

  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);

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Check the user exists first (GET the user list for the authorizer) or treat BasicSecurityDBResourceException as an idempotent no-op.
  2. Verify the authorizer prefix/name passed to deleteUser matches the one containing the user.
  3. Confirm exact username spelling and case (the map is case-sensitive as stored).
  4. Guard against double deletion in automation by tracking already-deleted users.

Example fix

// before
client.deleteUser(authorizerPrefix, userName);
// after: tolerate already-deleted
try {
  client.deleteUser(authorizerPrefix, userName);
} catch (BasicSecurityDBResourceException e) {
  LOG.info("User [%s] already absent in [%s]; skipping.", userName, authorizerPrefix);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check existence before deleting
Map<String, BasicAuthorizerUser> userMap = BasicAuthUtils.deserializeAuthorizerUserMap(
    objectMapper, getCurrentUserMapBytes(prefix));
if (!userMap.containsKey(userName)) {
  LOG.info("User [%s] absent from [%s]; nothing to delete", userName, prefix);
  return;
}

Type guard

boolean userExists(final String prefix, final String userName) {
  return BasicAuthUtils.deserializeAuthorizerUserMap(
      objectMapper, getCurrentUserMapBytes(prefix)).containsKey(userName);
}

Try / catch

try {
  updater.deleteUser(prefix, userName);
} catch (BasicSecurityDBResourceException e) {
  // idempotent delete: user already gone
  LOG.info("User [%s] already absent in authorizer [%s].", userName, prefix);
}

Prevention

When it happens

Trigger: Calling deleteUser for a userName that was never created, was already deleted, or does not exist under the specified authorizer prefix (e.g. deleting from the wrong authorizer name).

Common situations: Double-delete in cleanup scripts without existence checks; typo'd or case-mismatched usernames; targeting the wrong authorizer (e.g. 'internal' vs a custom one); users removed concurrently by another admin between the check and prior calls.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


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