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

  1. Fetch the mapping (GET .../groupMappings/<name>) and only delete roles it actually contains
  2. Treat this 400 as success in 'ensure role absent' idempotent flows
  3. Refresh cached group-mapping state before retrying
  4. 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

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


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