alibaba/nacos · info · IllegalArgumentException

user '{username}' already bound to the role '{role}'!

Error message

user '{username}' already bound to the role '{role}'!

What it means

Thrown by addRole when isUserBoundToRole(role, username) is already true, i.e. the user already holds that exact role. Nacos rejects duplicate role bindings to keep the role set unique and avoid redundant permission inheritance. The check runs after the reserved-role and existence checks, just before persisting.

Source

Thrown at plugin-default-impl/nacos-default-auth-plugin/src/main/java/com/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceDirectImpl.java:140

    
    @Override
    public void addRole(String role, String username) {
        if (userDetailsService.getUser(username) == null) {
            throw new IllegalArgumentException("user '" + username + "' not found!");
        }
        
        if (AuthConstants.GLOBAL_ADMIN_ROLE.equals(role)) {
            throw new IllegalArgumentException(
                "role '" + AuthConstants.GLOBAL_ADMIN_ROLE + "' is not permitted to create!");
        }
        
        if (AuthConstants.ANONYMOUS_ROLE.equals(role)) {
            throw new IllegalArgumentException(
                "role '" + AuthConstants.ANONYMOUS_ROLE + "' is reserved by the system");
        }
        
        if (isUserBoundToRole(role, username)) {
            throw new IllegalArgumentException(
                "user '" + username + "' already bound to the role '" + role + "'!");
        }
        
        rolePersistService.addRole(role, username);
        getCachedRoleSet().add(role);
        invalidateUserRoles(username);
    }
    
    @Override
    public void addAdminRole(String username) {
        if (userDetailsService.getUser(username) == null) {
            throw new IllegalArgumentException("user '" + username + "' not found!");
        }
        if (hasGlobalAdminRole()) {
            throw new IllegalArgumentException(
                "role '" + AuthConstants.GLOBAL_ADMIN_ROLE + "' already exist !");
        }
        

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Before granting, check roleService.getRoles(username) (or isUserBoundToRole) and skip if already present.
  2. Make provisioning idempotent: treat 'already bound' as success rather than retrying.
  3. Guard the UI/submit button against duplicate submissions.

Example fix

// before
roleService.addRole(role, username); // throws if already bound

// after
if (!roleService.isUserBoundToRole(role, username)) {
    roleService.addRole(role, username);
}
Defensive patterns

Strategy: validation

Validate before calling

if (roleService.isUserBoundToRole(role, username)) {
    // already granted; treat as success (idempotent)
    return;
}
roleService.addRole(role, username);

Prevention

When it happens

Trigger: Calling addRole for a role the user already has; double-submitting a role-grant form; a retry loop that re-runs a successful grant after a transient error; idempotency-unaware provisioning scripts.

Common situations: See trigger scenarios.

Related errors


AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14). Data as JSON: /api/errors/e103ac2be46f4a3b. Report an issue: GitHub.