apache/cassandra · error · UnauthorizedException
Only superusers can remove identity bindings from a role…
Error message
Only superusers can remove identity bindings from a role with superuser status
What it means
DropIdentityStatement.authorize prevents non-superusers from detaching an identity (e.g., an mTLS identity/authorization identity) that is bound to a role carrying superuser status, to avoid regular admins weakening superuser authentication. It throws UnauthorizedException when Roles.hasSuperuserStatus on the target role is true and the caller is not a superuser.
Solutions
- Perform the DROP IDENTITY as a superuser.
- First (as superuser) demote the target role: ALTER ROLE <role> WITH SUPERUSER = false, then remove the identity with the regular admin.
- Re-scope the automation to use a superuser credential only for identity-management operations.
Example fix
// before (non-superuser) DROP IDENTITY 'CN=svc,OU=sec'; // after (as superuser) DROP IDENTITY 'CN=svc,OU=sec'; // or, as superuser first: ALTER ROLE svc_role WITH SUPERUSER = false; -- then non-superuser admin may drop the identity
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check (as caller): only attempt identity drops with a superuser connection
if (!currentUserIsSuperuser(session) && identityBoundToSuperuserRole(identity)) throw new IllegalStateException('requires superuser'); Try / catch
try { session.execute(dropIdentityCql); } catch (UnauthorizedException e) { if (e.getMessage().contains("Only superusers can remove identity bindings")) { /* escalate to superuser connection or demote target role first */ } else throw e; } Prevention
- Route identity-management operations through superuser credentials
- Demote a role's SUPERUSER status before removing its identities with a regular admin account
- Document identity/role bindings in provisioning automation to avoid surprises
When it happens
Trigger: A non-superuser executes DROP IDENTITY <identity> where the identity is bound (roleForIdentity) to a role whose superuser status is true, as resolved before the check.
Common situations: Security teams rotating certificates/identities with an admin account that is not a superuser; automation with scoped credentials attempting identity cleanup; confusion between role-admin permissions and superuser-only identity operations.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/7124ab4088444dd7.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/cql3/statements/DropIdentityStatement.java:70
String roleForIdentity = DatabaseDescriptor.getRoleManager().roleForIdentity(identity);
if (roleForIdentity == null)
{
checkPermission(state, Permission.DROP, RoleResource.root());
}
else
{
// Check permission for the target role, i.e. we were granted permission to DROP ROLE
// for the target identity, this should allow us to drop the identity to role mapping
checkPermission(state, Permission.DROP, RoleResource.role(roleForIdentity));
if (!state.getUser().isSuper())
{
// If the current user is a regular user and the target role is an admin role
// we disallow the operation. Only a superuser can remove an identity bound to
// a role with superuser status
if (Roles.hasSuperuserStatus(RoleResource.role(roleForIdentity)))
throw new UnauthorizedException("Only superusers can remove identity bindings from a role with superuser status");
}
}
}
@Override
public void validate(ClientState state)
{
state.ensureNotAnonymous();
if (!ifExists && !DatabaseDescriptor.getRoleManager().isExistingIdentity(identity))
{
throw new InvalidRequestException(String.format("identity '%s' doesn't exist", identity));
}
}
@Override
public AuditLogContext getAuditLogContext()
{View on GitHub (pinned to 88fd0f6a0e)