alibaba/nacos · warning · IllegalArgumentException

role '__nacos_anonymous_role__' is reserved by the system

Error message

role '__nacos_anonymous_role__' is reserved by the system

What it means

Thrown by addRole when the role name equals AuthConstants.ANONYMOUS_ROLE ("__nacos_anonymous_role__"). That role is an internal system role used for unauthenticated/anonymous access mapping and is provisioned by the system itself; it cannot be created through the public role API. The check protects the internal anonymous permission set from being overwritten.

Source

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

        if (pageInfo == null) {
            return new Page<>();
        }
        return pageInfo;
    }
    
    @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!");
        }

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Remove __nacos_anonymous_role__ (and ROLE_ADMIN) from any role list you feed to addRole.
  2. Manage anonymous access through the system's authorization config, not by recreating the role.
  3. Add a guard that rejects role names matching AuthConstants.ANONYMOUS_ROLE before calling the API.

Example fix

// before
for (String r : roles) roleService.addRole(r, username); // blows up on anonymous

// after
Set<String> reserved = Set.of("ROLE_ADMIN", "__nacos_anonymous_role__");
roles.stream().filter(r -> !reserved.contains(r))
     .forEach(r -> roleService.addRole(r, username));
Defensive patterns

Strategy: validation

Validate before calling

Set<String> reserved = Set.of(AuthConstants.GLOBAL_ADMIN_ROLE, AuthConstants.ANONYMOUS_ROLE);
if (reserved.contains(role)) {
    throw new IllegalArgumentException("Refusing to create reserved system role: " + role);
}
roleService.addRole(role, username);

Prevention

When it happens

Trigger: Calling addRole("__nacos_anonymous_role__", username); importing an external role catalog that includes the anonymous role; a misconfigured role-sync job feeding literal reserved names.

Common situations: Bulk role import without a reserved-name blocklist; tools or Helm charts that attempt to recreate every default role; misunderstanding that the anonymous role is system-managed.

Related errors


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